zdzolton-cambric 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
data/cambric.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{cambric}
5
- s.version = "0.5.0"
5
+ s.version = "0.6.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Zachary Zolton", "Geoff Buesing"]
9
- s.date = %q{2009-05-29}
9
+ s.date = %q{2009-06-02}
10
10
  s.email = %q{zachary.zolton@gmail.com}
11
11
  s.extra_rdoc_files = [
12
12
  "LICENSE",
@@ -21,8 +21,18 @@ Gem::Specification.new do |s|
21
21
  "VERSION",
22
22
  "cambric.gemspec",
23
23
  "lib/cambric.rb",
24
- "spec/cambric_spec.rb",
24
+ "lib/cambric/assume_design_doc_name.rb",
25
+ "lib/cambric/configurator.rb",
26
+ "lib/cambric/test_helpers.rb",
27
+ "spec/cambric/assume_design_doc_name_spec.rb",
28
+ "spec/cambric/cambric_spec.rb",
29
+ "spec/cambric/configurator_spec.rb",
30
+ "spec/cambric/test_helpers_spec.rb",
25
31
  "spec/fixtures/twitter-clone/tweets/views/by_follower_and_created_at/map.js",
32
+ "spec/fixtures/twitter-clone/users/views/bad/map.js",
33
+ "spec/fixtures/twitter-clone/users/views/bad/reduce.js",
34
+ "spec/fixtures/twitter-clone/users/views/followers/map.js",
35
+ "spec/fixtures/twitter-clone/users/views/followers/reduce.js",
26
36
  "spec/spec.opts",
27
37
  "spec/spec_helper.rb"
28
38
  ]
@@ -32,7 +42,10 @@ Gem::Specification.new do |s|
32
42
  s.rubygems_version = %q{1.3.3}
33
43
  s.summary = %q{Opinionated management and usage of CouchDB from your Ruby apps.}
34
44
  s.test_files = [
35
- "spec/cambric_spec.rb",
45
+ "spec/cambric/assume_design_doc_name_spec.rb",
46
+ "spec/cambric/cambric_spec.rb",
47
+ "spec/cambric/configurator_spec.rb",
48
+ "spec/cambric/test_helpers_spec.rb",
36
49
  "spec/spec_helper.rb"
37
50
  ]
38
51
 
@@ -0,0 +1,15 @@
1
+ module Cambric
2
+ module AssumeDesignDocName
3
+
4
+ attr_accessor :cambric_design_doc_name
5
+
6
+ def view name, options={}, &block
7
+ super "#{@cambric_design_doc_name}/#{name}", options, &block
8
+ end
9
+
10
+ def cambric_design_doc
11
+ get "_design/#{@cambric_design_doc_name}"
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ module Cambric
2
+ class Configurator
3
+
4
+ attr_accessor :design_doc_name, :db_dir, :environment, :databases
5
+
6
+ def initialize
7
+ @design_doc_name = 'cambric'
8
+ @db_dir = 'cambric'
9
+ @environment = 'development'
10
+ @databases = {}
11
+ end
12
+
13
+ def initialize_databases
14
+ dbs_by_name = {}
15
+ @databases.each_pair do |db,urls_by_env|
16
+ urls_by_env.keys.map!{ |env| env.to_sym }
17
+ uri = URI.parse urls_by_env[@environment.to_sym]
18
+ dbs_by_name[db.to_sym] = initialize_database uri
19
+ end
20
+ dbs_by_name
21
+ end
22
+
23
+ private
24
+
25
+ def initialize_database uri
26
+ server = CouchRest.new("#{uri.scheme}://#{uri.host}:#{uri.port}")
27
+ database = server.database uri.path.gsub(/^\//, '')
28
+ database.extend ::Cambric::AssumeDesignDocName
29
+ database.cambric_design_doc_name = @design_doc_name
30
+ database
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,96 @@
1
+ require 'open3'
2
+
3
+ module Cambric
4
+ module TestHelpers
5
+
6
+ class ReduceError < RuntimeError ; end
7
+
8
+ def execute_map db, view, doc
9
+ view_functions = Cambric[db].cambric_design_doc['views'][view.to_s]
10
+ execute_js <<-CODE
11
+ var emittedKeyValuePairs = [];
12
+ function emit(key, value) {
13
+ emittedKeyValuePairs.push({ 'key': key, 'value': value });
14
+ }
15
+ var mapFunction = #{view_functions['map']};
16
+ try { mapFunction(#{doc.to_json}); } catch (e) {}
17
+ returnValueToRuby(emittedKeyValuePairs);
18
+ CODE
19
+ end
20
+
21
+ def execute_reduce db, view, options
22
+ view_functions = Cambric[db].cambric_design_doc['views'][view.to_s]
23
+ options = { :rereduce => false }.merge(options)
24
+ execute_js <<-CODE
25
+ var reduceFunction = #{view_functions['reduce']};
26
+ try {
27
+ returnValueToRuby(reduceFunction(
28
+ #{(options[:keys] || []).to_json},
29
+ #{(options[:values] || []).to_json},
30
+ #{options[:rereduce]}
31
+ ));
32
+ } catch (error) {
33
+ raiseErrorToRuby(error);
34
+ }
35
+ CODE
36
+ end
37
+
38
+ private
39
+
40
+ def execute_js code
41
+ output = nil
42
+ Open3.popen3 'js' do |stdin, stdout, stderr|
43
+ stdin.puts HELPER_FUNCTIONS
44
+ stdin.puts code
45
+ stdin.close
46
+ output = JSON.parse(stdout.read)
47
+ end
48
+ raise ReduceError, output['error'] if output.has_key?('error')
49
+ output['result']
50
+ end
51
+
52
+ HELPER_FUNCTIONS = <<-HELPERS
53
+ function sum(values) {
54
+ var total = 0;
55
+ values.forEach(function(v) { total += v; });
56
+ return total;
57
+ }
58
+
59
+ function toJSON(obj) {
60
+ switch (typeof obj) {
61
+ case 'object':
62
+ if (obj) {
63
+ var list = [];
64
+ if (obj instanceof Array) {
65
+ for (var i=0;i < obj.length;i++) {
66
+ list.push(toJSON(obj[i]));
67
+ }
68
+ return '[' + list.join(',') + ']';
69
+ } else {
70
+ for (var prop in obj) {
71
+ list.push('"' + prop + '":' + toJSON(obj[prop]));
72
+ }
73
+ return '{' + list.join(',') + '}';
74
+ }
75
+ } else {
76
+ return 'null';
77
+ }
78
+ case 'string':
79
+ return '"' + obj.replace(/(["'])/g, '\\'') + '"';
80
+ case 'number':
81
+ case 'boolean':
82
+ return new String(obj);
83
+ }
84
+ }
85
+
86
+ function returnValueToRuby(value) {
87
+ print(toJSON({ 'result': value }));
88
+ }
89
+
90
+ function raiseErrorToRuby(error) {
91
+ print(toJSON({ 'error': error['message'] }));
92
+ }
93
+ HELPERS
94
+
95
+ end
96
+ end
data/lib/cambric.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'couchrest'
2
2
  require 'uri'
3
3
 
4
+ Dir.glob(File.join(File.dirname(__FILE__), 'cambric/**.rb')).each{ |f| require f }
5
+
4
6
  module Cambric
5
7
 
6
8
  def self.design_doc_name
@@ -66,46 +68,3 @@ private
66
68
  end
67
69
 
68
70
  end
69
-
70
- class Cambric::Configurator
71
-
72
- attr_accessor :design_doc_name, :db_dir, :environment, :databases
73
-
74
- def initialize
75
- @design_doc_name = 'cambric'
76
- @db_dir = 'cambric'
77
- @environment = 'development'
78
- @databases = {}
79
- end
80
-
81
- def initialize_databases
82
- dbs_by_name = {}
83
- @databases.each_pair do |db,urls_by_env|
84
- urls_by_env.keys.map!{ |env| env.to_sym }
85
- uri = URI.parse urls_by_env[@environment.to_sym]
86
- dbs_by_name[db.to_sym] = initialize_database uri
87
- end
88
- dbs_by_name
89
- end
90
-
91
- private
92
-
93
- def initialize_database uri
94
- server = CouchRest.new("#{uri.scheme}://#{uri.host}:#{uri.port}")
95
- database = server.database uri.path.gsub(/^\//, '')
96
- database.extend ::Cambric::AssumeDesignDocName
97
- database.cambric_design_doc_name = @design_doc_name
98
- database
99
- end
100
-
101
- end
102
-
103
- module Cambric::AssumeDesignDocName
104
-
105
- attr_accessor :cambric_design_doc_name
106
-
107
- def view name, options={}, &block
108
- super "#{@cambric_design_doc_name}/#{name}", options, &block
109
- end
110
-
111
- end
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
2
+
3
+ describe Cambric::AssumeDesignDocName do
4
+
5
+ describe "after pushing design docs" do
6
+ before :all do
7
+ configure_twitter_clone
8
+ Cambric.prepare_databases!
9
+ end
10
+
11
+ after(:all){ delete_twitter_clone_databases }
12
+
13
+ it "should be able to query view without re-specifying design doc name" do
14
+ Cambric[:tweets].view 'by_follower_and_created_at'
15
+ end
16
+
17
+ it "should get the design doc specified by configuration" do
18
+ Cambric[:tweets].cambric_design_doc.should_not be_nil
19
+ end
20
+ end
21
+
22
+ end
@@ -1,30 +1,6 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
2
2
  require 'uri'
3
3
 
4
- TWITTER_CLONE_DATABASES = {
5
- :users => {
6
- :development => 'http://127.0.0.1:5984/users-development',
7
- :test => 'http://127.0.0.1:5984/users-testing'
8
- },
9
- :tweets => {
10
- :development => 'http://127.0.0.1:5984/tweets-development',
11
- :test => 'http://127.0.0.1:5984/tweets-testing'
12
- }
13
- }
14
-
15
- def configure_twitter_clone
16
- Cambric.configure do |config|
17
- config.design_doc_name = 'twitter-clone'
18
- config.db_dir = 'spec/fixtures/twitter-clone'
19
- config.environment = 'test'
20
- config.databases = TWITTER_CLONE_DATABASES
21
- end
22
- end
23
-
24
- def delete_twitter_clone_databases
25
- %w(users tweets).each{ |db| Cambric[db].delete! rescue nil }
26
- end
27
-
28
4
  describe Cambric do
29
5
 
30
6
  describe "after configuring without a block" do
@@ -141,10 +117,6 @@ describe Cambric do
141
117
  design_doc = Cambric[:tweets].get("_design/twitter-clone")
142
118
  design_doc['views']['by_follower_and_created_at'].should_not be_nil
143
119
  end
144
-
145
- it "should be able to query view without re-specifying design doc name" do
146
- Cambric[:tweets].view 'by_follower_and_created_at'
147
- end
148
120
  end
149
121
 
150
122
  describe "when the design doc already exists" do
@@ -211,34 +183,3 @@ describe Cambric do
211
183
  end
212
184
 
213
185
  end
214
-
215
- describe Cambric::Configurator do
216
- before :all do
217
- @config = Cambric::Configurator.new
218
- end
219
-
220
- it "should default its design doc name to 'cambric'" do
221
- @config.design_doc_name.should == 'cambric'
222
- end
223
-
224
- it "should default to 'cambric' for the database directory" do
225
- @config.db_dir.should == 'cambric'
226
- end
227
-
228
- it "should default to 'development' for the environment" do
229
- @config.environment.should == 'development'
230
- end
231
-
232
- describe "when retrieving the configured CouchRest::Database instances" do
233
- before :all do
234
- @config.databases = TWITTER_CLONE_DATABASES
235
- @dbs = @config.initialize_databases
236
- end
237
-
238
- it "should have the expected URLs for development environment" do
239
- %w(users tweets).each do |db|
240
- @dbs[db.to_sym].uri.should == "http://127.0.0.1:5984/#{db}-development"
241
- end
242
- end
243
- end
244
- end
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
2
+
3
+ describe Cambric::Configurator do
4
+ before :all do
5
+ @config = Cambric::Configurator.new
6
+ end
7
+
8
+ it "should default its design doc name to 'cambric'" do
9
+ @config.design_doc_name.should == 'cambric'
10
+ end
11
+
12
+ it "should default to 'cambric' for the database directory" do
13
+ @config.db_dir.should == 'cambric'
14
+ end
15
+
16
+ it "should default to 'development' for the environment" do
17
+ @config.environment.should == 'development'
18
+ end
19
+
20
+ describe "when retrieving the configured CouchRest::Database instances" do
21
+ before :all do
22
+ @config.databases = TWITTER_CLONE_DATABASES
23
+ @dbs = @config.initialize_databases
24
+ end
25
+
26
+ it "should have the expected URLs for development environment" do
27
+ %w(users tweets).each do |db|
28
+ @dbs[db.to_sym].uri.should == "http://127.0.0.1:5984/#{db}-development"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
2
+
3
+ describe Cambric::TestHelpers do
4
+
5
+ include Cambric::TestHelpers
6
+
7
+ before :all do
8
+ configure_twitter_clone
9
+ Cambric.prepare_databases!
10
+ end
11
+
12
+ after(:all){ delete_twitter_clone_databases }
13
+
14
+ describe "testing a view's map function" do
15
+ it "should return an array, with an element for each call to emit()" do
16
+ kv_pairs = execute_map :users, :followers,
17
+ '_id' => 'poddle', 'following' => ['jack', 'russel']
18
+ kv_pairs.size.should == 2
19
+ kv_pairs[0]['key'].should == 'jack'
20
+ kv_pairs[1]['key'].should == 'russel'
21
+ kv_pairs[0]['value'].should == 'poddle'
22
+ kv_pairs[1]['value'].should == 'poddle'
23
+ end
24
+
25
+ it "should return an empty array when documents cause an exception" do
26
+ kv_pairs = execute_map :users, :bad, "doesn't" => 'matter'
27
+ kv_pairs.length.should == 1
28
+ end
29
+ end
30
+
31
+ describe "testing a view's reduce function" do
32
+
33
+ it "should default to false for rereduce" do
34
+ result = execute_reduce :users, :followers, :values => ['dr', 'quinn', 'medicine', 'woman']
35
+ result.should == 4
36
+ end
37
+
38
+ it "should be able to execute a rereduce" do
39
+ result = execute_reduce :users, :followers, :values => [4, 5, 6], :rereduce => true
40
+ result.should == 15
41
+ end
42
+
43
+ it "should forward exceptions to Ruby" do
44
+ lambda do
45
+ execute_reduce :users, :bad, :values => ['what', 'ever']
46
+ end.should raise_error(Cambric::TestHelpers::ReduceError)
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,7 @@
1
+ function(doc) {
2
+ // This should be returned:
3
+ emit(4, 5);
4
+
5
+ // This should not:
6
+ emit(doc['some_non_existant_property'].length, null);
7
+ }
@@ -0,0 +1,4 @@
1
+ function(key, values) {
2
+ // This should throw an error:
3
+ return values[0]['some_non_existant_property'].length;
4
+ }
@@ -0,0 +1,6 @@
1
+ function(doc) {
2
+ // FYI: This example assumes we're using usernames as the doc ID.
3
+ doc['following'].forEach(function(user) {
4
+ emit(user, doc['_id']);
5
+ });
6
+ }
@@ -0,0 +1,7 @@
1
+ function(keys, values, rereduce) {
2
+ if (rereduce) {
3
+ return sum(values);
4
+ } else {
5
+ return values.length;
6
+ }
7
+ }
data/spec/spec_helper.rb CHANGED
@@ -12,12 +12,34 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
12
12
 
13
13
  require 'cambric'
14
14
 
15
- FIXTURES_PATH = './spec/fixtures'
15
+ ##############
16
16
 
17
- def load_fixture *path
18
- open(File.expand_path(File.join %w(spec fixtures) + path))
17
+ TWITTER_CLONE_DATABASES = {
18
+ :users => {
19
+ :development => 'http://127.0.0.1:5984/users-development',
20
+ :test => 'http://127.0.0.1:5984/users-testing'
21
+ },
22
+ :tweets => {
23
+ :development => 'http://127.0.0.1:5984/tweets-development',
24
+ :test => 'http://127.0.0.1:5984/tweets-testing'
25
+ }
26
+ }
27
+
28
+ def configure_twitter_clone
29
+ Cambric.configure do |config|
30
+ config.design_doc_name = 'twitter-clone'
31
+ config.db_dir = 'spec/fixtures/twitter-clone'
32
+ config.environment = 'test'
33
+ config.databases = TWITTER_CLONE_DATABASES
34
+ end
35
+ end
36
+
37
+ def delete_twitter_clone_databases
38
+ %w(users tweets).each{ |db| Cambric[db].delete! rescue nil }
19
39
  end
20
40
 
41
+ #############
42
+
21
43
  Spec::Runner.configure do |config|
22
44
 
23
45
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zdzolton-cambric
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zachary Zolton
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-05-29 00:00:00 -07:00
13
+ date: 2009-06-02 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -41,8 +41,18 @@ files:
41
41
  - VERSION
42
42
  - cambric.gemspec
43
43
  - lib/cambric.rb
44
- - spec/cambric_spec.rb
44
+ - lib/cambric/assume_design_doc_name.rb
45
+ - lib/cambric/configurator.rb
46
+ - lib/cambric/test_helpers.rb
47
+ - spec/cambric/assume_design_doc_name_spec.rb
48
+ - spec/cambric/cambric_spec.rb
49
+ - spec/cambric/configurator_spec.rb
50
+ - spec/cambric/test_helpers_spec.rb
45
51
  - spec/fixtures/twitter-clone/tweets/views/by_follower_and_created_at/map.js
52
+ - spec/fixtures/twitter-clone/users/views/bad/map.js
53
+ - spec/fixtures/twitter-clone/users/views/bad/reduce.js
54
+ - spec/fixtures/twitter-clone/users/views/followers/map.js
55
+ - spec/fixtures/twitter-clone/users/views/followers/reduce.js
46
56
  - spec/spec.opts
47
57
  - spec/spec_helper.rb
48
58
  has_rdoc: false
@@ -72,5 +82,8 @@ signing_key:
72
82
  specification_version: 3
73
83
  summary: Opinionated management and usage of CouchDB from your Ruby apps.
74
84
  test_files:
75
- - spec/cambric_spec.rb
85
+ - spec/cambric/assume_design_doc_name_spec.rb
86
+ - spec/cambric/cambric_spec.rb
87
+ - spec/cambric/configurator_spec.rb
88
+ - spec/cambric/test_helpers_spec.rb
76
89
  - spec/spec_helper.rb