Skip to content

Commit

Permalink
Update examples
Browse files Browse the repository at this point in the history
Added two new examples:

1) Asynchronous API server with Couchbase backend using Goliath and
   Grape framework

2) Gzip transcoder, the demonstration of writing custom client-side
   data filters for Couchbase (in addition to three built-in:
   Document, Marshal and Plain)

Change-Id: Id35f083fe6bf33e34cb914754695e23849a78c11
Reviewed-on: http://review.couchbase.org/26153
Tested-by: Sergey Avseyev <sergey.avseyev@gmail.com>
Reviewed-by: Matt Ingenthron <matt@couchbase.com>
  • Loading branch information
avsej committed May 7, 2013
1 parent db1d9cd commit 79743e8
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 0 deletions.
5 changes: 5 additions & 0 deletions examples/chat-goliath-grape/Gemfile
@@ -0,0 +1,5 @@
source 'https://rubygems.org'

gem 'couchbase'
gem 'goliath'
gem 'grape'
50 changes: 50 additions & 0 deletions examples/chat-goliath-grape/README.markdown
@@ -0,0 +1,50 @@
# Couchbase + Grape + Goliath = <3

This demo shows how to integrate these cool things to build
asynchronous REST service

# Usage

1. Clone the repository. Navigate to `examples/chat-goliath-grape/`
directory

2. Install libcouchbase (http://www.couchbase.com/develop/c/current).
For MacOS users it will look like:

brew update
brew install libcouchbase

3. Install all ruby dependencies. This demo has been tested on latest
stable version of ruby (`ruby 2.0.0p0 (2013-02-24 revision 39474)
[x86_64-linux]`)

gem install bundler
bundle install

4. Setup your Couchbase server
(http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-getting-started.html).
It should have `default` bucket. Or you can edit connection options
in `config/app.rb` file.

5. Start the server up

ruby app.rb -sv

6. Create an message:

$ curl -X POST -Fmessage="Hello world" http://localhost:9000/messages
{"ok":true,"id":"msg:1","cas":11880713153673363456}

7. If you create a design document called `messages` and put a view
named `all` with this map function:

function(doc, meta) {
if (doc.timestamp) {
emit(meta.id, doc)
}
}

You can fetch all messages with this command:

curl -X GET http://localhost:9000/messages
{"ok":true,"messages":[{"id":"msg:1","key":"msg:1","value":{"timestamp":"2013-04-11T12:43:42+03:00","message":"Hello world"},"cas":11880713153673363456}]}
67 changes: 67 additions & 0 deletions examples/chat-goliath-grape/app.rb
@@ -0,0 +1,67 @@
# Author:: Couchbase <info@couchbase.com>
# Copyright:: 2013 Couchbase, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'em-synchrony'
require 'couchbase'
require 'goliath'
require 'grape'
require 'date'

class Chat < Grape::API

format :json

resource 'messages' do
get do
view = env.couchbase.design_docs["messages"].all(:include_docs => true)
msgs = view.map do |r|
{
"id" => r.id,
"key" => r.key,
"value" => r.value,
"cas" => r.meta["cas"],
# "doc" => r.doc
}
end
{"ok" => true, "messages" => msgs}
end

post do
payload = {
"timestamp" => DateTime.now.iso8601,
"message" => params["message"]
}
id = env.couchbase.incr("msgid", :initial => 1)
id = "msg:#{id}"
cas = env.couchbase.set(id, payload)
{"ok" => true, "id" => id, "cas" => cas}
end
end

end

class App < Goliath::API
def response(env)
Chat.call(env)
rescue => e
[
500,
{'Content-Type' => 'application/json'},
MultiJson.dump(:error => e, :stacktrace => e.backtrace)
]
end
end
20 changes: 20 additions & 0 deletions examples/chat-goliath-grape/config/app.rb
@@ -0,0 +1,20 @@
# Author:: Couchbase <info@couchbase.com>
# Copyright:: 2013 Couchbase, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

config['couchbase'] = EventMachine::Synchrony::ConnectionPool.new(:size => 5) do
Couchbase::Bucket.new(:engine => :eventmachine)
end
3 changes: 3 additions & 0 deletions examples/transcoders/Gemfile
@@ -0,0 +1,3 @@
source 'https://rubygems.org'

gem 'couchbase'
59 changes: 59 additions & 0 deletions examples/transcoders/README.markdown
@@ -0,0 +1,59 @@
# Gzip Transcoder

This example demonstrates advanced usage of client-side transcoders.
In particular it uses GzipWriter and GzipReader classes from ruby
standard library to keep documents compressed. The example shows basic
technique, so that it can be used as a starting point to write your
custom data filters and adaptors.

## Usage

1. Clone the repository. Navigate to directory `examples/transcoders`.

2. Install libcouchbase (http://www.couchbase.com/develop/c/current).
For MacOS users it will look like:

brew update
brew install libcouchbase

3. Install all ruby dependencies. This demo has been tested on latest
stable version of ruby (`ruby 2.0.0p0 (2013-02-24 revision 39474)
[x86_64-linux]`)

gem install bundler
bundle install

4. Setup your Couchbase server
(http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-getting-started.html).
It should have `default` bucket. Or you can specify different
bucket using CLI options.

5. Store some file to the cluster:

$ ./cb-zcp Gemfile
Run with arguments: {:bucket=>"default", :hostname=>"127.0.0.1", :port=>8091, :username=>nil, :password=>nil}
store "Gemfile" ... ok

6. Get the data back:

$ ./cb-zcat Gemfile
Run with arguments: {:bucket=>"default", :hostname=>"127.0.0.1", :port=>8091, :username=>nil, :password=>nil}
Gemfile:
source 'https://rubygems.org'

gem 'couchbase'

7. To make sure that the data is in compressed state, you can check it in `irb`:

$ bundle exec irb
2.0.0p0 (main):001:0> require 'couchbase'
true
2.0.0p0 (main):002:0> conn = Couchbase.connect(:transcoder => nil)
#<Couchbase::Bucket:0x007fa344886fc8 "http://localhost:8091/pools/default/buckets/default/" transcoder=nil, default_flags=0x0, quiet=false, connected=true, timeout=2500000>
2.0.0p0 (main):003:0> File.open("Gemfile.gz", "w+"){|f| f.write(conn.get("Gemfile"))}
65

$ zcat Gemfile.gz
source 'https://rubygems.org'

gem 'couchbase'
40 changes: 40 additions & 0 deletions examples/transcoders/cb-zcat
@@ -0,0 +1,40 @@
#!/usr/bin/env ruby
# Author:: Couchbase <info@couchbase.com>
# Copyright:: 2013 Couchbase, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'bundler'
Bundler.setup

require 'couchbase'

$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'gzip_transcoder'
require 'options'

STDERR.puts "Run with arguments: #{OPTIONS.inspect}"
begin
conn = Couchbase.connect(OPTIONS)
conn.transcoder = GzipTranscoder.new

ARGV.each do |filename|
STDERR.puts "#{filename}:"
STDOUT.puts(conn.get(filename))
end
rescue Couchbase::Error::Base => ex
STDERR.puts "ERROR: #{ex}"
exit 1
end
45 changes: 45 additions & 0 deletions examples/transcoders/cb-zcp
@@ -0,0 +1,45 @@
#!/usr/bin/env ruby
# Author:: Couchbase <info@couchbase.com>
# Copyright:: 2013 Couchbase, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'bundler'
Bundler.setup

require 'couchbase'

$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'gzip_transcoder'
require 'options'

STDERR.puts "Run with arguments: #{OPTIONS.inspect}"
begin
conn = Couchbase.connect(OPTIONS)
conn.transcoder = GzipTranscoder.new

ARGV.each do |filename|
STDERR.print "store \"#{filename}\" ... "
if File.exists?(filename)
conn.set(filename, File.read(filename))
STDERR.puts "ok"
else
STDERR.puts "not found"
end
end
rescue Couchbase::Error::Base => ex
STDERR.puts "ERROR: #{ex}"
exit 1
end
49 changes: 49 additions & 0 deletions examples/transcoders/gzip_transcoder.rb
@@ -0,0 +1,49 @@
# Author:: Couchbase <info@couchbase.com>
# Copyright:: 2013 Couchbase, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'zlib'
require 'stringio'

# This class wraps any other transcoder and performs compression
# using zlib
class GzipTranscoder
FMT_GZIP = 0x04

def initialize(base = nil)
@base = base || Couchbase::Transcoder::Plain
end

def dump(obj, flags, options = {})
obj, flags = @base.dump(obj, flags, options)
io = StringIO.new
gz = Zlib::GzipWriter.new(io)
gz.write(obj)
gz.close
[io.string, flags|FMT_GZIP]
end

def load(blob, flags, options = {})
# decompress value only if gzip flag set
if (flags & FMT_GZIP) == FMT_GZIP
io = StringIO.new(blob)
gz = Zlib::GzipReader.new(io)
blob = gz.read
gz.close
end
@base.load(blob, flags, options)
end
end

0 comments on commit 79743e8

Please sign in to comment.