ShyCouch 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ GEM
10
10
  bundler (~> 1.0)
11
11
  git (>= 1.2.5)
12
12
  rake
13
- rake (0.8.7)
13
+ rake (0.9.2)
14
14
  rcov (0.9.10)
15
15
  ruby2ruby (1.2.5)
16
16
  ruby_parser (~> 2.0)
data/README CHANGED
@@ -1,15 +1,29 @@
1
1
  = ShyCouch
2
2
 
3
- ShyCouch is a Ruby library for CouchDB. It's a data persistence layer that uses native objects and a simple query language, designed primarily to replace ActiveRecord in Camping. A Python version is forthcoming.
3
+ ShyCouch is a Ruby library for CouchDB. It's a data persistence layer that uses native objects and lets you write native Ruby blocks that'll be parsed into MapReduce JavaScript functions.
4
4
 
5
- I'm mainly putting this up for feedback at the moment. It's a complete mess.
5
+ The structure is a bit of a mess, but essentially it provides:
6
6
 
7
- Eventually I'm gonna re-release it with all of Camping's code, with AR and stuff removed and this stuff put in.
7
+ - a database object
8
+ - a CouchDocument object, which is a glorified hash
9
+ - a Design object representing the Design document for an app in CouchDB
8
10
 
9
- ShyCouch does not come with an elaborate query language. It'll automatically create an 'all' query for all your models (don't use it unless you have a really good excuse not to write a custom query, doing the filtering at the application layer is hell slow), but aside from that you have to write your own map and reduce functions.
11
+ At the moment, all views have to be written manually. I'm currently evaluating the wisdom of building a query language to dynamically write views and cache them in Couch.
10
12
 
11
13
  = Usage
12
14
 
15
+ require 'shycouch'
16
+ settings = {
17
+ "db"=> {
18
+ "host" => "ramponeau.local",
19
+ "port" => 5984,
20
+ "name" => "food",
21
+ "user" => "cerales",
22
+ "password" => "password"
23
+ },
24
+ }
25
+ db = ShyCouch::CouchDatabase()
26
+
13
27
  == Models
14
28
 
15
29
  You don't need to define entity relationships or anything. Your models can just look like this:
data/Rakefile CHANGED
@@ -44,7 +44,8 @@ end
44
44
 
45
45
  task :default => :test
46
46
 
47
- require 'rake/rdoctask'
47
+ # require 'rake/rdoctask'
48
+ require 'rdoc/task'
48
49
  Rake::RDocTask.new do |rdoc|
49
50
  version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
51
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ShyCouch}
8
- s.version = "0.3.5"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = [%q{Shy Inc.}, %q{Daniel Bryan}, %q{Cerales}]
12
- s.date = %q{2011-08-20}
12
+ s.date = %q{2011-08-28}
13
13
  s.description = %q{Ruby API for CouchDB, designed to work with the Camping micro-framework.}
14
14
  s.email = %q{danbryan@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -37,7 +37,8 @@ Gem::Specification.new do |s|
37
37
  "test/test_couchdb_api.rb",
38
38
  "test/test_couchdb_factory.rb",
39
39
  "test/test_design_documents.rb",
40
- "test/test_fields.rb"
40
+ "test/test_fields.rb",
41
+ "test/test_views.rb"
41
42
  ]
42
43
  s.homepage = %q{http://github.com/Cerales/ShyCouch}
43
44
  s.licenses = [%q{MIT}]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.5
1
+ 0.4.0
@@ -11,63 +11,37 @@ $:.unshift(File.dirname(__FILE__)) unless
11
11
  require 'net/http'
12
12
  require 'json'
13
13
  require 'resolv'
14
- require 'shyrubyjs'
14
+ # require 'shyrubyjs'
15
+ require '~/dev/gems/ShyRubyJS/lib/ShyRubyJS'
15
16
  # require everything from the 'ShyCouch' subdirectory
16
17
  Dir.new(File.dirname(__FILE__)+'/ShyCouch').each { |f| require 'shycouch/' + f.split('.')[0] unless f == '.' or f == '..' }
17
18
 
18
19
 
19
20
  module ShyCouch
20
21
  class << self
21
- def goes(m)
22
- Camping.goes m
23
- c = %{
24
- #{m.to_s}::Models::CouchDocument = ShyCouch::Data::CouchDocument
25
- }
26
- eval(c)
27
- ShyCouch.create
28
- end
29
22
 
30
- def create(settings=nil)
23
+ def create(settings=nil) #TODO - change this
31
24
  $couchdb = ShyCouch.getDB(settings)
32
25
  end
33
26
 
34
27
  def getDB(settings=nil)
35
- settings = $settings unless settings
36
- database = CouchDBAPI.new(settings["db"]["host"], settings["db"]["port"], settings["db"]["name"], settings["db"]["user"], settings["db"]["password"])
37
- puts database.connect unless database.connect["ok"]
28
+ settings = $couch_settings unless settings
29
+ database = CouchDatabase.new(settings)
30
+ puts database.connect unless database.connect["ok"] #TODO - hm
38
31
  database.create unless database.on_server?
39
32
  return database
40
33
  end
41
34
 
42
35
  end
43
36
  attr_accessor :database
44
-
45
- class Connection
46
- # Test that the database is accessible and give back a CouchDBAPI object if so.
47
- # Doesn't actually gets instantiated - is just here to allow nice ShyCouch::Connection.Create syntax
48
- # def self.Create(settings=nil)
49
- # settings = $settings unless settings
50
- # database = CouchDBAPI.new(settings["db"]["host"], settings["db"]["port"], settings["db"]["name"], settings["db"]["user"], settings["db"]["password"])
51
- # puts database.connect unless database.connect["ok"]
52
- # database.create unless database.on_server?
53
- # return database
54
- # end
55
-
56
- def push_generic_views
57
- #TODO
58
- end
59
- end
60
37
 
61
- class CouchDBAPI
62
- def initialize(host, port, name, user, password)
63
- @host, @port, @name, @user, @password = host, port, name, user, password
64
- @views = []
65
- @server = CouchServerConnection.allocate
38
+ class ShyCouchError < StandardError; end
39
+
40
+ class CouchDatabase
41
+ def initialize(settings)
42
+ # args = explode_settings(args) if args.size == 1
43
+ init(settings)
66
44
  end
67
- #
68
- # def initialize(*settings)
69
- # @host, @port, @name, @user, @password = settings["db"]["host"], settings["db"]["port"], settings["db"]["name"], settings["db"]["user"], settings["db"]["password"]
70
- # end
71
45
 
72
46
  attr_accessor :server, :name, :host, :port, :views
73
47
 
@@ -134,6 +108,14 @@ module ShyCouch
134
108
  end
135
109
 
136
110
  private
111
+
112
+ def init(settings)
113
+ db_settings = settings["db"]
114
+ @host, @port, @name, @user, @password = db_settings["host"],db_settings["port"], db_settings["name"],db_settings["user"], db_settings["password"]
115
+ @views = []
116
+ @server = CouchServerConnection.allocate
117
+ end
118
+
137
119
  class CouchServerConnection
138
120
  def initialize(args, options=nil)#host, port, user, password, options = nil)
139
121
  @host = args["host"]
@@ -237,9 +219,13 @@ module ShyCouch
237
219
 
238
220
  private
239
221
 
240
- def handle_error(req, res)
222
+ def handle_failure(req, res)
241
223
  raise RuntimeError.new("#{res.code}:#{res.message}\nMETHOD:#{req.method}\nURI:#{req.path}\n#{res.body}")
242
224
  end
225
+
226
+ def handle_error(e)
227
+ raise RuntimeError.new("#{e.inspect}\n Maybe be due to illegal rev or id change")
228
+ end
243
229
 
244
230
  def request(req)
245
231
  res = Net::HTTP.start(@host, @port) { |http|
@@ -247,11 +233,13 @@ module ShyCouch
247
233
  http.request(req)
248
234
  }
249
235
  unless res.kind_of?(Net::HTTPSuccess)
250
- handle_error(req, res)
236
+ handle_failure(req, res)
251
237
  end
252
238
  res
239
+ rescue Errno::ECONNRESET => e
240
+ handle_error(e)
253
241
  end
254
242
  end
255
-
243
+
256
244
  end
257
245
  end
@@ -5,9 +5,9 @@ module ShyCouch
5
5
  class CouchDocument < Hash
6
6
  class << self
7
7
  # allows instance.class.requirements to be called
8
- attr_accessor :requirements
9
8
  end
10
-
9
+ @@needs, @@suggests = [], []
10
+
11
11
  def initialize(hash={})
12
12
  # Assumes that the "kind" is the class name unless explicitly stated otherwise
13
13
  # TODO - maybe just force it to be the class name no matter what tbh
@@ -17,27 +17,30 @@ module ShyCouch
17
17
  # super(hash)
18
18
  end
19
19
 
20
- # def initialize(hash=nil, requirements)
21
- # @requirements = requirements
22
- # merge!(hash) if hash
23
- # raise TypeError unless valid? #TODO - should raise a more specific and useful error
24
- # end
25
-
26
20
  def self.all
27
- database = CouchDatabase.new($settings)
28
- database.get()
29
21
  end
30
-
31
- def self.requires(*requirements)
32
- @requirements = requirements
22
+
23
+ def self.needs(*requirements)
24
+ requirements.map { |requirement| @@needs << requirement } unless requirements.empty?
25
+ return @@needs
33
26
  end
34
-
35
- def add_key(key, value=nil)
36
- # The attr value assignment operator has been overriden, but it checks for the existence of a key.
37
- # And therefore the user has to explicitly call this method first.
38
- self[key] = value
27
+
28
+ def self.suggests(*suggestions)
29
+ suggestions.map { |suggestion| @@suggests << suggestion } unless suggestions.empty?
30
+ return @@suggests
39
31
  end
40
-
32
+
33
+ def needs;self.class.needs; end
34
+ def suggests; self.class.suggests; end
35
+
36
+ def needs?(requirement)
37
+ @@needs.include?(requirement) ? true : false
38
+ end
39
+
40
+ def suggests?(requirement)
41
+ @@suggests.include?(requirement) ? true : false
42
+ end
43
+
41
44
  def attr_keys
42
45
  # returns the keys for all the attrs that aren't the id or rev
43
46
  attr_keys = []
@@ -90,15 +93,54 @@ module ShyCouch
90
93
  end
91
94
 
92
95
  end
96
+
97
+ class View
98
+ attr_accessor :map, :reduce, :name
99
+
100
+ def initialize(view_name, &block)
101
+ @parser = ShyRubyJS::ShySexpParser.new
102
+ sexp_check = block.to_sexp
103
+ sexp = block.to_sexp(:strip_enclosure=>true)
104
+
105
+ # make sure the two blocks inside are calls to "map" and "reduce"
106
+
107
+ @name = view_name.to_s
108
+ if sexp[0] == :block
109
+ unless sexp_check[3][1][1][2] == :map and sexp_check[3][2][1][2] == :reduce
110
+ raise ShyCouchError, "view must be called with map block and optional reduce block"
111
+ end
112
+ [1,2].each { |num|
113
+ 2.times { sexp[num].delete_at(1) }
114
+ }
115
+ @map = @parser.parse(sexp[1])[0]
116
+ @reduce = @parser.parse(sexp[2]) if sexp[2].length > 1
117
+ elsif sexp[0] == :iter
118
+ raise ShyCouchError, "view must be called with map block and optional reduce block" unless sexp[1][2] == :map
119
+ @map = @parser.parse(sexp[3])
120
+ end
121
+ end
122
+
123
+ def as_hash
124
+ h = {}
125
+ h[@name] = {"map" => @map}
126
+ h.merge!({"reduce" => @reduce}) if @reduce
127
+ return h
128
+ end
129
+ end
93
130
 
94
131
  class Design < CouchDocument
95
132
  # this is used to manage design documents
96
133
  # In practise, the Controllers should be a list of classes corresponding to design documents
97
-
98
- def map(&block);end
99
-
100
- def reduce(&block);end
101
-
134
+
135
+ def initialize(name)
136
+ merge! "_id" => "_design/#{name.to_s}"
137
+ @parser = ShyRubyJS::ShySexpParser.new
138
+ end
139
+
140
+ def self.setup
141
+ # setup_all_view
142
+ end
143
+
102
144
  def push;end #must override push in order to set the ID
103
145
  end
104
146
 
@@ -7,7 +7,8 @@ require_relative '../lib/ShyCouch'
7
7
  # Settings for a database that is set up and working, with an admin user
8
8
  $settings = {
9
9
  "db"=> {
10
- "host" => "localhost",
10
+ "host" => "ramponeau.local",
11
+ # "host" => "localhost",
11
12
  "port" => 5984,
12
13
  "name" => "test",
13
14
  "user" => "cerales",
@@ -29,4 +30,6 @@ require_relative 'test_camping_integration'
29
30
 
30
31
  require_relative 'test_couchdb_factory'
31
32
 
32
- require_relative 'test_design_documents'
33
+ require_relative 'test_design_documents'
34
+
35
+ require_relative 'test_views'
@@ -38,7 +38,7 @@ class CouchDocumentTests# < Test::Unit::TestCase
38
38
  def test_create_from_fixnum
39
39
  # Ensure no doc creation with fixnum argument
40
40
  fixnum = 1
41
- assert_raises TypeError do
41
+ assert_raise TypeError do
42
42
  doc = ShyCouch::Data::CouchDocument.new(fixnum)
43
43
  end
44
44
  end
@@ -110,9 +110,9 @@ class CouchDocumentTests# < Test::Unit::TestCase
110
110
  @existing_valid_documents.each { |doc|
111
111
  # add some more attributes
112
112
  assert(doc._rev)
113
- doc.add_key("owner", "the guvvmint")
114
- doc.add_key("buttonCount")
115
- doc.add_key("friends")
113
+ doc["owner"] = "the guvvmint"
114
+ doc["buttonCount"] = nil
115
+ doc["friends"] = nil
116
116
  doc.buttonCount = 5
117
117
  doc.friends = ["alan", "alex", "all me other mates"]
118
118
 
@@ -132,7 +132,7 @@ class CouchDocumentTests# < Test::Unit::TestCase
132
132
  def test_illegal_change_to_rev
133
133
  @existing_valid_documents.each { |doc|
134
134
  doc._rev = "hurr"
135
- assert_raises RuntimeError do
135
+ assert_raise RuntimeError do
136
136
  res = doc.push
137
137
  end
138
138
  }
@@ -0,0 +1,51 @@
1
+ require 'test/unit'
2
+ require_relative '../lib/ShyCouch.rb'
3
+
4
+ class CouchViewTests < Test::Unit::TestCase
5
+ JS_MAP_FUNCTION_HEADER = "function ( doc ) { \n "
6
+ JS_REDUCE_FUNCTION_HEADER = "function(key, values, rereduce)"
7
+ JS_FUNCTION_FOOTER = "}"
8
+ def setup
9
+ @couch_views = []
10
+ end
11
+
12
+ def view(view_name, &block)
13
+ @couch_views << ShyCouch::Data::View.new(view_name, &block)
14
+ end
15
+ def teardown; end
16
+
17
+ def test_define_map_view
18
+ view :five_star_butts do
19
+ map do
20
+ def function(doc)
21
+ emit(doc) if doc.kind == "butt" and doc.star_rating == 5
22
+ end
23
+ end
24
+ end
25
+ expected_js = JS_MAP_FUNCTION_HEADER + %{if( doc.kind == "butt" && doc.star_rating == 5 ) {\n emit(doc)\n} \n } + JS_FUNCTION_FOOTER
26
+ assert_equal(expected_js, @couch_views[0].map)
27
+ # puts @couch_views[0].map
28
+ end
29
+
30
+ def test_define_map_and_reduce_view
31
+ view :beggar_count do
32
+ map do
33
+ def function(doc)
34
+ emit(doc) if doc.kind == "beggar"
35
+ end
36
+ end
37
+ reduce do
38
+ def function(key, values, rereduce)
39
+ return sum(values)
40
+ end
41
+ end
42
+ end
43
+ puts @couch_views[0].map
44
+ puts @couch_views[0].reduce
45
+ expected_map = JS_MAP_FUNCTION_HEADER + %{if( doc.kind == "beggar" ) {\n emit(doc)\n} \n } + JS_FUNCTION_FOOTER
46
+ expected_rejuce = JS_REDUCE_FUNCTION_HEADER
47
+ assert_equal(expected_map, @couch_views[0].map)
48
+
49
+ end
50
+
51
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ShyCouch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2011-08-20 00:00:00.000000000Z
14
+ date: 2011-08-28 00:00:00.000000000Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
18
- requirement: &70214897246860 !ruby/object:Gem::Requirement
18
+ requirement: &70288155812040 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ~>
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: 1.0.0
24
24
  type: :development
25
25
  prerelease: false
26
- version_requirements: *70214897246860
26
+ version_requirements: *70288155812040
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: jeweler
29
- requirement: &70214897243320 !ruby/object:Gem::Requirement
29
+ requirement: &70288155810560 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ~>
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: 1.6.4
35
35
  type: :development
36
36
  prerelease: false
37
- version_requirements: *70214897243320
37
+ version_requirements: *70288155810560
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: rcov
40
- requirement: &70214897240520 !ruby/object:Gem::Requirement
40
+ requirement: &70288155807920 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :development
47
47
  prerelease: false
48
- version_requirements: *70214897240520
48
+ version_requirements: *70288155807920
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: sourcify
51
- requirement: &70214897238760 !ruby/object:Gem::Requirement
51
+ requirement: &70288155806340 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ~>
@@ -56,10 +56,10 @@ dependencies:
56
56
  version: 0.5.0
57
57
  type: :development
58
58
  prerelease: false
59
- version_requirements: *70214897238760
59
+ version_requirements: *70288155806340
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: ShyRubyJS
62
- requirement: &70214897237620 !ruby/object:Gem::Requirement
62
+ requirement: &70288155792060 !ruby/object:Gem::Requirement
63
63
  none: false
64
64
  requirements:
65
65
  - - ! '>='
@@ -67,10 +67,10 @@ dependencies:
67
67
  version: '0'
68
68
  type: :development
69
69
  prerelease: false
70
- version_requirements: *70214897237620
70
+ version_requirements: *70288155792060
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: ShyRubyJS
73
- requirement: &70214897236100 !ruby/object:Gem::Requirement
73
+ requirement: &70288155790660 !ruby/object:Gem::Requirement
74
74
  none: false
75
75
  requirements:
76
76
  - - ! '>='
@@ -78,10 +78,10 @@ dependencies:
78
78
  version: '0'
79
79
  type: :runtime
80
80
  prerelease: false
81
- version_requirements: *70214897236100
81
+ version_requirements: *70288155790660
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: sourcify
84
- requirement: &70214897235140 !ruby/object:Gem::Requirement
84
+ requirement: &70288155789540 !ruby/object:Gem::Requirement
85
85
  none: false
86
86
  requirements:
87
87
  - - ! '>='
@@ -89,7 +89,7 @@ dependencies:
89
89
  version: '0'
90
90
  type: :runtime
91
91
  prerelease: false
92
- version_requirements: *70214897235140
92
+ version_requirements: *70288155789540
93
93
  description: Ruby API for CouchDB, designed to work with the Camping micro-framework.
94
94
  email: danbryan@gmail.com
95
95
  executables: []
@@ -119,6 +119,7 @@ files:
119
119
  - test/test_couchdb_factory.rb
120
120
  - test/test_design_documents.rb
121
121
  - test/test_fields.rb
122
+ - test/test_views.rb
122
123
  homepage: http://github.com/Cerales/ShyCouch
123
124
  licenses:
124
125
  - MIT
@@ -134,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
135
  version: '0'
135
136
  segments:
136
137
  - 0
137
- hash: 3382610228249582822
138
+ hash: 1563418179645536452
138
139
  required_rubygems_version: !ruby/object:Gem::Requirement
139
140
  none: false
140
141
  requirements: