relaxdb 0.3.5 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.textile +21 -23
  2. data/Rakefile +2 -7
  3. data/docs/spec_results.html +5 -5
  4. data/lib/more/grapher.rb +1 -1
  5. data/lib/relaxdb.rb +3 -5
  6. data/lib/relaxdb/all_delegator.rb +19 -13
  7. data/lib/relaxdb/document.rb +150 -218
  8. data/lib/relaxdb/extlib.rb +7 -1
  9. data/lib/relaxdb/migration.rb +11 -8
  10. data/lib/relaxdb/net_http_server.rb +19 -1
  11. data/lib/relaxdb/paginator.rb +30 -11
  12. data/lib/relaxdb/query.rb +1 -1
  13. data/lib/relaxdb/{belongs_to_proxy.rb → references_proxy.rb} +3 -3
  14. data/lib/relaxdb/relaxdb.rb +87 -7
  15. data/lib/relaxdb/server.rb +8 -2
  16. data/lib/relaxdb/taf2_curb_server.rb +2 -1
  17. data/lib/relaxdb/uuid_generator.rb +38 -2
  18. data/lib/relaxdb/view_by_delegator.rb +34 -0
  19. data/lib/relaxdb/view_object.rb +1 -1
  20. data/lib/relaxdb/view_uploader.rb +16 -2
  21. data/lib/relaxdb/views.rb +23 -55
  22. data/readme.rb +3 -3
  23. data/spec/all_delegator_spec.rb +52 -0
  24. data/spec/callbacks_spec.rb +4 -4
  25. data/spec/derived_properties_spec.rb +4 -4
  26. data/spec/design_doc_spec.rb +2 -2
  27. data/spec/doc_inheritable_spec.rb +2 -2
  28. data/spec/document_spec.rb +47 -25
  29. data/spec/migration_spec.rb +12 -10
  30. data/spec/qpaginate_spec.rb +88 -0
  31. data/spec/query_spec.rb +2 -2
  32. data/spec/references_proxy_spec.rb +94 -0
  33. data/spec/relaxdb_spec.rb +29 -21
  34. data/spec/server_spec.rb +4 -3
  35. data/spec/spec_helper.rb +1 -0
  36. data/spec/spec_models.rb +48 -57
  37. data/spec/uuid_generator_spec.rb +34 -0
  38. data/spec/view_by_spec.rb +62 -54
  39. data/spec/view_docs_by_spec.rb +85 -0
  40. metadata +38 -27
  41. data/lib/more/atomic_bulk_save_support.rb +0 -18
  42. data/lib/relaxdb/has_many_proxy.rb +0 -101
  43. data/lib/relaxdb/has_one_proxy.rb +0 -42
  44. data/lib/relaxdb/references_many_proxy.rb +0 -97
  45. data/spec/belongs_to_spec.rb +0 -124
  46. data/spec/has_many_spec.rb +0 -202
  47. data/spec/has_one_spec.rb +0 -123
  48. data/spec/references_many_spec.rb +0 -173
  49. data/spec/view_spec.rb +0 -23
data/README.textile CHANGED
@@ -1,16 +1,15 @@
1
1
  h3. What's New?
2
2
 
3
- * 2009-08-15
4
- ** A few tweaks, patches and fixes push the version to 0.3.5, compatible with CouchDB 0.9.1 and the 0.10 branch.
5
- ** The Rails error_messages_for helper is now supported. Thanks to "Balint Erdi":http://github.com/balinterdi.
6
- * 2009-05-27
7
- ** Added minimal support for data migrations. Although CouchDB's nature removes the necessity for migrations, certain knowledge that all objects possess a particular property can simplify client logic. This desire for simplification is the rationale behind this change.
8
- * 2009-04-19
9
- ** Defaults to taf2-curb, falling back to Net/HTTP if it taf2-curb can't be loaded. Thanks to "Fred Cheung":http://www.spacevatican.org/2009/4/13/fun-with-ruby-http-clients.
10
- ** For those interested in using RelaxDB with an ETag based cache, "look here":http://github.com/fcheung/relaxdb/commit/1d9acfd5f6b3c23da0d275252b6a6e064865440e
11
-
12
- * 2009-03-31
13
- ** RelaxDB 0.3 released - compatible with CouchDB 0.9.
3
+ * 2010-04-10
4
+ ** RelaxDB 0.4 released. Supports Ruby 1.9.1 and CouchDB 0.11.0.
5
+ ** Auto-generated views no longer emit the document as a value by default
6
+ ** Erlang view shorthand supported e.g. _sum and _count
7
+ ** Added single query pagination
8
+ ** Performance improvements
9
+ ** Time.to_json fix. Thanks to "Karmi":http://github.com/karmi
10
+ ** *Note*: This release includes a number of breaking changes. Please see the "release notes":http://github.com/paulcarey/relaxdb/blob/master/RELEASE_NOTES.textile for upgrading notes.
11
+
12
+ For those interested in using RelaxDB with an ETag based cache, please see "Fred Cheung's work":http://github.com/fcheung/relaxdb/commit/1d9acfd5f6b3c23da0d275252b6a6e064865440e
14
13
 
15
14
  h2. Overview
16
15
 
@@ -22,8 +21,6 @@ A basic merb plugin, "merb_relaxdb":http://github.com/paulcarey/merb_relaxdb/tre
22
21
 
23
22
  For more complete documentation take a look at docs/spec_results.html and the corresponding specs.
24
23
 
25
- *Note*: While RelaxDB 0.3 is explicitly compatible with CouchDB 0.9, HEAD typically tracks CouchDB HEAD.
26
-
27
24
  h2. Details
28
25
 
29
26
  h3. Getting started
@@ -36,7 +33,9 @@ h3. Getting started
36
33
  RelaxDB.configure :host => "localhost", :port => 5984, :design_doc => "app"
37
34
  RelaxDB.use_db "relaxdb_scratch"
38
35
 
39
- RelaxDB.enable_view_creation # creates views when class definition is executed
36
+ RelaxDB.enable_view_creation # creates views when class definition is executed
37
+
38
+ RelaxDB::View.design_doc.save # save views to CouchDB after executing class definitions
40
39
  </code>
41
40
  </pre>
42
41
 
@@ -65,8 +64,9 @@ class Invite < RelaxDB::Document
65
64
  property :sender_name,
66
65
  :derived => [:sender, lambda { |p, o| o.sender.name } ]
67
66
 
68
- view_by :sender_name
69
- view_by :sender_id
67
+ view_by :sender_name # Emits 1 as the map value
68
+ view_docs_by :sender_id # Emits the doc as the map value
69
+
70
70
  view_by :recipient_id, :created_at, :descending => true
71
71
 
72
72
  def on_update_conflict
@@ -95,10 +95,11 @@ i.save!
95
95
  il = RelaxDB.load i._id
96
96
  puts i == il # true
97
97
 
98
- ir = Invite.by_sender_name "sofa"
98
+ ir = Invite.by_sender_name "sofa"
99
99
  puts i == ir # true
100
100
 
101
- ix = Invite.by_sender_name(:key => "sofa").first
101
+ ix_ids = Invite.by_sender_name :key => "sofa"
102
+ ix = ix_ids.load!.first
102
103
  puts i == ix # true
103
104
 
104
105
  # Denormalization
@@ -162,12 +163,9 @@ h3. Creating views by hand
162
163
  emit(doc.state, doc);
163
164
  }
164
165
 
166
+ // Uses the CouchDB builtin to invoke an Erlang reduce fun
165
167
  function Invites_by_state-reduce(keys, values, rereduce) {
166
- if (rereduce) {
167
- return sum(values);
168
- } else {
169
- return values.length;
170
- }
168
+ _count
171
169
  }
172
170
  $
173
171
 
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'spec/rake/spectask'
4
4
 
5
5
  PLUGIN = "relaxdb"
6
6
  NAME = "relaxdb"
7
- GEM_VERSION = "0.3.5"
7
+ GEM_VERSION = "0.5"
8
8
  AUTHOR = "Paul Carey"
9
9
  EMAIL = "paul.p.carey@gmail.com"
10
10
  HOMEPAGE = "http://github.com/paulcarey/relaxdb/"
@@ -37,7 +37,7 @@ end
37
37
 
38
38
  desc "Install"
39
39
  task :install => [:package] do
40
- sh %{sudo gem install --local pkg/#{NAME}-#{GEM_VERSION} --no-update-sources}
40
+ sh %{gem install --no-rdoc --no-ri --local pkg/#{NAME}-#{GEM_VERSION}}
41
41
  end
42
42
 
43
43
  desc "Run specs"
@@ -51,11 +51,6 @@ Spec::Rake::SpecTask.new('spec:html') do |t|
51
51
  t.spec_opts = ["--format", "html:docs/spec_results.html"]
52
52
  end
53
53
 
54
- desc "Supports atomic bulk save"
55
- task :atomic_bulk_save_support do |t|
56
- require 'lib/more/atomic_bulk_save_support.rb'
57
- end
58
-
59
54
  desc "Create base spec db"
60
55
  task :create_base_db do
61
56
  require 'spec/spec_helper'
@@ -183,7 +183,7 @@ a {
183
183
  </div>
184
184
  <div class="example_group">
185
185
  <dl>
186
- <dt id="example_group_2">RelaxDB::BelongsToProxy belongs_to</dt>
186
+ <dt id="example_group_2">RelaxDB::BelongsToProxy references</dt>
187
187
  <script type="text/javascript">moveProgressBar('0.4');</script>
188
188
  <dd class="spec passed"><span class="passed_spec_name">should return nil when accessed before assignment</span></dd>
189
189
  <script type="text/javascript">moveProgressBar('0.9');</script>
@@ -208,7 +208,7 @@ a {
208
208
  </div>
209
209
  <div class="example_group">
210
210
  <dl>
211
- <dt id="example_group_3">RelaxDB::BelongsToProxy belongs_to validator</dt>
211
+ <dt id="example_group_3">RelaxDB::BelongsToProxy references validator</dt>
212
212
  <script type="text/javascript">moveProgressBar('5.0');</script>
213
213
  <dd class="spec passed"><span class="passed_spec_name">should be passed the _id and object</span></dd>
214
214
  <script type="text/javascript">moveProgressBar('5.5');</script>
@@ -620,7 +620,7 @@ a {
620
620
  <dl>
621
621
  <dt id="example_group_42">RelaxDB::HasManyProxy has_many#delete</dt>
622
622
  <script type="text/javascript">moveProgressBar('51.8');</script>
623
- <dd class="spec passed"><span class="passed_spec_name">should nullify the belongs_to relationship</span></dd>
623
+ <dd class="spec passed"><span class="passed_spec_name">should nullify the references relationship</span></dd>
624
624
  </dl>
625
625
  </div>
626
626
  <div class="example_group">
@@ -988,12 +988,12 @@ a {
988
988
  </div>
989
989
  <div class="example_group">
990
990
  <dl>
991
- <dt id="example_group_80">view_by</dt>
991
+ <dt id="example_group_80">view_docs_by</dt>
992
992
  </dl>
993
993
  </div>
994
994
  <div class="example_group">
995
995
  <dl>
996
- <dt id="example_group_81">view_by view_by</dt>
996
+ <dt id="example_group_81">view_docs_by view_docs_by</dt>
997
997
  <script type="text/javascript">moveProgressBar('93.5');</script>
998
998
  <dd class="spec passed"><span class="passed_spec_name">should create corresponding views</span></dd>
999
999
  <script type="text/javascript">moveProgressBar('93.9');</script>
data/lib/more/grapher.rb CHANGED
@@ -30,7 +30,7 @@ module RelaxDB
30
30
 
31
31
  dot << %Q%#{doc._id} [ label ="#{atts}"];\n%
32
32
 
33
- doc.class.belongs_to_rels.each do |relationship, opts|
33
+ doc.class.references_rels.each do |relationship, opts|
34
34
  id = doc.instance_variable_get("@#{relationship}_id".to_sym)
35
35
  dot << %Q%#{id} -> #{doc._id} [ label = "#{relationship}"];\n% if id
36
36
  end
data/lib/relaxdb.rb CHANGED
@@ -6,7 +6,7 @@ require 'uuid'
6
6
  require 'cgi'
7
7
  require 'net/http'
8
8
  require 'logger'
9
- require 'parsedate'
9
+ require 'parsedate' if RUBY_VERSION.to_f < 1.9
10
10
  require 'pp'
11
11
  require 'tempfile'
12
12
 
@@ -24,20 +24,18 @@ rescue LoadError
24
24
  end
25
25
 
26
26
  require 'relaxdb/all_delegator'
27
- require 'relaxdb/belongs_to_proxy'
28
27
  require 'relaxdb/design_doc'
29
28
  require 'relaxdb/document'
30
29
  require 'relaxdb/extlib'
31
- require 'relaxdb/has_many_proxy'
32
- require 'relaxdb/has_one_proxy'
33
30
  require 'relaxdb/migration'
34
31
  require 'relaxdb/paginate_params'
35
32
  require 'relaxdb/paginator'
36
33
  require 'relaxdb/query'
37
- require 'relaxdb/references_many_proxy'
34
+ require 'relaxdb/references_proxy'
38
35
  require 'relaxdb/relaxdb'
39
36
  require 'relaxdb/server'
40
37
  require 'relaxdb/uuid_generator'
38
+ require 'relaxdb/view_by_delegator'
41
39
  require 'relaxdb/view_object'
42
40
  require 'relaxdb/view_result'
43
41
  require 'relaxdb/view_uploader'
@@ -15,10 +15,21 @@ module RelaxDB
15
15
  end
16
16
 
17
17
  def __getobj__
18
- unless @objs
19
- @objs = RelaxDB.rf_view "#{@class_name}_all", @params
18
+ unless @ids
19
+ params = {:raw => true}.merge @params
20
+ result = RelaxDB.rf_view "#{@class_name}_all", params
21
+ @ids = RelaxDB.ids_from_view result
20
22
  end
21
- @objs
23
+ @ids
24
+ end
25
+
26
+ def __setobj__ obj
27
+ # Intentionally empty
28
+ end
29
+
30
+ def load!
31
+ __getobj__
32
+ @objs = RelaxDB.load! @ids
22
33
  end
23
34
 
24
35
  def size
@@ -26,17 +37,12 @@ module RelaxDB
26
37
  size || 0
27
38
  end
28
39
 
29
- # TODO: destroy in a bulk_save if feasible
30
40
  def destroy!
31
- __getobj__
32
- @objs.each do |o|
33
- # A reload is required for deleting objects with a self referential references_many relationship
34
- # This makes all.destroy! very slow. Change if needed
35
- # obj = RelaxDB.load(o._id)
36
- # obj.destroy!
37
-
38
- o.destroy!
39
- end
41
+ load!
42
+ @objs.each { |o| o.data["_deleted"] = true }
43
+ # Direct post rather than bulk save as we don't want validators to be run
44
+ resp = RelaxDB.db.post("_bulk_docs", {"docs" => @objs}.to_json)
45
+ JSON.parse resp.body
40
46
  end
41
47
 
42
48
  end
@@ -8,41 +8,57 @@ module RelaxDB
8
8
  attr_accessor :errors
9
9
 
10
10
  # A call issued to save_all will save this object and the
11
- # contents of the save_list. This allows secondary object to
11
+ # contents of the save_list. This allows secondary objects to
12
12
  # be saved at the same time as this object.
13
13
  attr_accessor :save_list
14
14
 
15
15
  # Attribute symbols added to this list won't be validated on save
16
16
  attr_accessor :validation_skip_list
17
17
 
18
+ # Not part of the public API - should only be used by clients with caution.
19
+ # The data keys are Strings as this is what JSON.parse gives us.
20
+ attr_accessor :data
21
+
18
22
  class_inheritable_accessor :properties, :reader => true
19
23
  self.properties = []
20
24
 
21
25
  class_inheritable_accessor :derived_prop_writers
22
26
  self.derived_prop_writers = {}
23
27
 
28
+ class_inheritable_accessor :__view_docs_by_list__
29
+ self.__view_docs_by_list__ = []
30
+
24
31
  class_inheritable_accessor :__view_by_list__
25
- self.__view_by_list__ = []
32
+ self.__view_by_list__ = []
26
33
 
27
- class_inheritable_accessor :belongs_to_rels, :reader => true
28
- self.belongs_to_rels = {}
34
+ class_inheritable_accessor :references_rels, :reader => true
35
+ self.references_rels = {}
29
36
 
30
37
  def self.property(prop, opts={})
31
38
  properties << prop
32
-
33
- define_method(prop) do
34
- instance_variable_get("@#{prop}".to_sym)
35
- end
39
+
40
+ if prop.to_s =~ /_at$|_on$|_date$|_time$/
41
+ define_method(prop) do
42
+ val = @data[prop.to_s]
43
+ Time.parse(val).utc rescue val
44
+ end
45
+ else
46
+ define_method(prop) do
47
+ @data[prop.to_s]
48
+ end
49
+ end
36
50
 
37
51
  define_method("#{prop}=") do |val|
38
- instance_variable_set("@#{prop}".to_sym, val)
52
+ @data[prop.to_s] = val
39
53
  end
40
54
 
41
55
  if opts[:default]
42
- define_method("set_default_#{prop}") do
43
- default = opts[:default]
44
- default = default.is_a?(Proc) ? default.call : default
45
- instance_variable_set("@#{prop}".to_sym, default)
56
+ define_method("__set_default_#{prop}__") do
57
+ if @data[prop.to_s].nil?
58
+ default = opts[:default]
59
+ val = default.is_a?(Proc) ? default.call : default
60
+ @data[prop.to_s] = val
61
+ end
46
62
  end
47
63
  end
48
64
 
@@ -69,11 +85,15 @@ module RelaxDB
69
85
  v.arity == 1 ?
70
86
  define_method(method_name) { |att_val| v.call(att_val) } :
71
87
  define_method(method_name) { |att_val| v.call(att_val, self) }
72
- elsif instance_methods.include? "validator_#{v}"
73
- define_method(method_name) { |att_val| send("validator_#{v}", att_val, self) }
74
88
  else
75
- define_method(method_name) { |att_val| send(v, att_val) }
76
- end
89
+ v_meths = instance_methods.select { |m| m =~ /validator_/ }
90
+ v_meths.map! { |m| m.to_sym } if RUBY_VERSION.to_f < 1.9
91
+ if v_meths.include? "validator_#{v}".to_sym
92
+ define_method(method_name) { |att_val| send("validator_#{v}", att_val, self) }
93
+ else
94
+ define_method(method_name) { |att_val| send(v, att_val) }
95
+ end
96
+ end
77
97
  end
78
98
 
79
99
  def self.create_validation_msg(att, validation_msg)
@@ -82,7 +102,7 @@ module RelaxDB
82
102
  define_method("#{att}_validation_msg") { |att_val| validation_msg.call(att_val) } :
83
103
  define_method("#{att}_validation_msg") { |att_val| validation_msg.call(att_val, self) }
84
104
  else
85
- define_method("#{att}_validation_msg") { validation_msg }
105
+ define_method("#{att}_validation_msg") { |att_val| validation_msg }
86
106
  end
87
107
  end
88
108
 
@@ -105,62 +125,64 @@ module RelaxDB
105
125
  if writers
106
126
  writers.each do |prop, writer|
107
127
  current_val = send(prop)
108
- begin
128
+ # begin
109
129
  send("#{prop}=", writer.call(current_val, self))
110
- rescue => e
111
- RelaxDB.logger.error "Deriving #{prop} from #{source} raised #{e}"
112
- end
130
+ # rescue => e
131
+ # RelaxDB.logger.error "Deriving #{prop} from #{source} raised #{e}"
132
+ # end
113
133
  end
114
134
  end
115
135
  end
116
136
 
117
- def initialize(hash={})
118
- unless hash["_id"]
119
- self._id = UuidGenerator.uuid
120
- end
121
-
137
+ def initialize(hash={})
122
138
  @errors = Errors.new
123
139
  @save_list = []
124
140
  @validation_skip_list = []
125
-
126
- # Set default properties if this object isn't being loaded from CouchDB
127
- unless hash["_rev"]
141
+
142
+ # hash.dup because assigning references properties and defaults both
143
+ # modify the internal representation - @data. This messes with the
144
+ # iterator below that assigns vals to @data.
145
+ params = hash.dup
146
+
147
+ # If there's no rev, it's a new document
148
+ if hash["_rev"].nil?
149
+ # Clients may use symbols as keys so convert all to strings first.
150
+ @data = hash.map { |k,v| [k.to_s, v] }.to_hash
151
+ else
152
+ @data = hash
153
+ end
154
+
155
+ unless @data["_id"]
156
+ @data["_id"] = UuidGenerator.uuid
157
+ end
158
+
159
+ # It's a new doc, set default properties. We only do this after ensuring
160
+ # this obj first has an _id.
161
+ unless @data["_rev"]
162
+ default_methods = methods.select { |m| m =~ /__set_default/ }
163
+ default_methods.map! { |m| m.to_sym } if RUBY_VERSION.to_f < 1.9
128
164
  properties.each do |prop|
129
- if methods.include?("set_default_#{prop}")
130
- send("set_default_#{prop}")
131
- end
165
+ if default_methods.include? "__set_default_#{prop}__".to_sym
166
+ send("__set_default_#{prop}__")
167
+ end
132
168
  end
133
- end
134
-
135
- @set_derived_props = hash["_rev"] ? false : true
136
- set_attributes(hash)
137
- @set_derived_props = true
138
- end
139
-
140
- def set_attributes(data)
141
- data.each do |key, val|
142
- # Only set instance variables on creation - object references are resolved on demand
143
-
144
- # If the variable name ends in _at, _on or _date try to convert it to a Time
145
- if [/_at$/, /_on$/, /_date$/, /_time$/].inject(nil) { |i, r| i ||= (key =~ r) }
146
- val = Time.parse(val).utc rescue val
169
+
170
+ params.each do |key, val|
171
+ send("#{key}=".to_sym, val)
147
172
  end
148
173
 
149
- # Ignore param keys that don't have a corresponding writer
150
- # This allows us to comfortably accept a hash containing superflous data
151
- # such as a params hash in a controller
152
- send("#{key}=".to_sym, val) if methods.include? "#{key}="
174
+ @data["relaxdb_class"] = self.class.name
153
175
  end
154
- end
155
-
176
+ end
177
+
156
178
  def inspect
157
179
  s = "#<#{self.class}:#{self.object_id}"
158
180
  properties.each do |prop|
159
- prop_val = instance_variable_get("@#{prop}".to_sym)
181
+ prop_val = @data[prop.to_s]
160
182
  s << ", #{prop}: #{prop_val.inspect}" if prop_val
161
183
  end
162
- self.class.belongs_to_rels.each do |relationship, opts|
163
- id = instance_variable_get("@#{relationship}_id".to_sym)
184
+ self.class.references_rels.each do |relationship, opts|
185
+ id = @data["#{relationship}_id"]
164
186
  s << ", #{relationship}_id: #{id}" if id
165
187
  end
166
188
  s << ", errors: #{errors.inspect}" unless errors.empty?
@@ -170,19 +192,12 @@ module RelaxDB
170
192
 
171
193
  alias_method :to_s, :inspect
172
194
 
173
- def to_json
174
- data = {}
175
- self.class.belongs_to_rels.each do |relationship, opts|
176
- id = instance_variable_get("@#{relationship}_id".to_sym)
177
- data["#{relationship}_id"] = id if id
178
- end
179
- properties.each do |prop|
180
- prop_val = instance_variable_get("@#{prop}".to_sym)
181
- data["#{prop}"] = prop_val if prop_val
182
- end
183
- data["errors"] = errors unless errors.empty?
184
- data["relaxdb_class"] = self.class.name
185
- data.to_json
195
+ def to_json
196
+ ref_rels = self.class.references_rels.map { |k, v| k.to_s }
197
+ @data.delete_if { |k,v| ref_rels.include? k }
198
+
199
+ @data["errors"] = errors unless errors.empty?
200
+ @data.to_json
186
201
  end
187
202
 
188
203
  # Not yet sure of final implemention for hooks - may lean more towards DM than AR
@@ -251,18 +266,20 @@ module RelaxDB
251
266
 
252
267
  def validates?
253
268
  props = properties - validation_skip_list
254
- prop_vals = props.map { |prop| instance_variable_get("@#{prop}") }
269
+ prop_vals = props.map { |prop| @data[prop.to_s] }
255
270
 
256
- rels = self.class.belongs_to_rels.keys - validation_skip_list
257
- rel_vals = rels.map { |rel| instance_variable_get("@#{rel}_id") }
271
+ rels = self.class.references_rels.keys - validation_skip_list
272
+ rel_vals = rels.map { |rel| @data["#{rel}_id"] }
258
273
 
259
274
  att_names = props + rels
260
275
  att_vals = prop_vals + rel_vals
261
276
 
262
- total_success = true
277
+ total_success = true
278
+ validate_methods = methods.select { |m| m =~ /validate_/ }
279
+ validate_methods.map! { |m| m.to_sym } if RUBY_VERSION.to_f < 1.9
263
280
  att_names.each_index do |i|
264
281
  att_name, att_val = att_names[i], att_vals[i]
265
- if methods.include? "validate_#{att_name}"
282
+ if validate_methods.include? "validate_#{att_name}".to_sym
266
283
  total_success &= validate_att(att_name, att_val)
267
284
  end
268
285
  end
@@ -280,7 +297,9 @@ module RelaxDB
280
297
  end
281
298
 
282
299
  unless success
283
- if methods.include? "#{att_name}_validation_msg"
300
+ v_msg_meths = methods.select { |m | m =~ /_validation_msg/ }
301
+ v_msg_meths.map! { |m| m.to_sym } if RUBY_VERSION.to_f < 1.9
302
+ if v_msg_meths.include? "#{att_name}_validation_msg".to_sym
284
303
  begin
285
304
  @errors[att_name] = send("#{att_name}_validation_msg", att_val)
286
305
  rescue => e
@@ -296,7 +315,7 @@ module RelaxDB
296
315
  end
297
316
 
298
317
  def new_document?
299
- @_rev.nil?
318
+ self._rev.nil?
300
319
  end
301
320
  alias_method :new_record?, :new_document?
302
321
  alias_method :unsaved?, :new_document?
@@ -310,10 +329,10 @@ module RelaxDB
310
329
  now = Time.now
311
330
  if new_document? && respond_to?(:created_at)
312
331
  # Don't override it if it's already been set
313
- @created_at = now if @created_at.nil?
332
+ @data["created_at"] = now if @data["created_at"].nil?
314
333
  end
315
334
 
316
- @updated_at = now if respond_to?(:updated_at)
335
+ @data["updated_at"] = now if respond_to?(:updated_at)
317
336
  end
318
337
 
319
338
  def create_or_get_proxy(klass, relationship, opts=nil)
@@ -330,117 +349,28 @@ module RelaxDB
330
349
  def ==(other)
331
350
  other && _id == other._id
332
351
  end
333
-
334
- # If you're using this method, read the specs and make sure you understand
335
- # how it can be used and how it shouldn't be used
336
- def self.references_many(relationship, opts={})
337
- # Treat the representation as a standard property
338
- properties << relationship
339
-
340
- # Keep track of the relationship so peers can be disassociated on destroy
341
- @references_many_rels ||= []
342
- @references_many_rels << relationship
343
-
344
- id_arr_sym = "@#{relationship}".to_sym
345
-
346
- if RelaxDB.create_views?
347
- target_class = opts[:class]
348
- relationship_as_viewed_by_target = opts[:known_as].to_s
349
- ViewCreator.references_many(self.name, relationship, target_class, relationship_as_viewed_by_target).save
350
- end
351
-
352
- define_method(relationship) do
353
- instance_variable_set(id_arr_sym, []) unless instance_variable_defined? id_arr_sym
354
- create_or_get_proxy(ReferencesManyProxy, relationship, opts)
355
- end
356
-
357
- define_method("#{relationship}_ids") do
358
- instance_variable_set(id_arr_sym, []) unless instance_variable_defined? id_arr_sym
359
- instance_variable_get(id_arr_sym)
360
- end
361
-
362
- define_method("#{relationship}=") do |val|
363
- # Don't invoke this method unless you know what you're doing
364
- instance_variable_set(id_arr_sym, val)
365
- end
366
- end
367
-
368
- def self.references_many_rels
369
- @references_many_rels ||= []
370
- end
371
-
372
- def self.has_many(relationship, opts={})
373
- @has_many_rels ||= []
374
- @has_many_rels << relationship
375
-
376
- if RelaxDB.create_views?
377
- target_class = opts[:class] || relationship.to_s.singularize.camel_case
378
- relationship_as_viewed_by_target = (opts[:known_as] || self.name.snake_case).to_s
379
- ViewCreator.has_n(self.name, relationship, target_class, relationship_as_viewed_by_target).save
380
- end
381
-
382
- define_method(relationship) do
383
- create_or_get_proxy(HasManyProxy, relationship, opts)
384
- end
385
-
386
- define_method("#{relationship}=") do |children|
387
- create_or_get_proxy(HasManyProxy, relationship, opts).children = children
388
- write_derived_props(relationship) if @set_derived_props
389
- children
390
- end
391
- end
392
-
393
- def self.has_many_rels
394
- # Don't force clients to check its instantiated
395
- @has_many_rels ||= []
396
- end
397
-
398
- def self.has_one(relationship)
399
- @has_one_rels ||= []
400
- @has_one_rels << relationship
401
-
402
- if RelaxDB.create_views?
403
- target_class = relationship.to_s.camel_case
404
- relationship_as_viewed_by_target = self.name.snake_case
405
- ViewCreator.has_n(self.name, relationship, target_class, relationship_as_viewed_by_target).save
406
- end
407
-
408
- define_method(relationship) do
409
- create_or_get_proxy(HasOneProxy, relationship).target
410
- end
411
-
412
- define_method("#{relationship}=") do |new_target|
413
- create_or_get_proxy(HasOneProxy, relationship).target = new_target
414
- write_derived_props(relationship) if @set_derived_props
415
- new_target
416
- end
417
- end
418
-
419
- def self.has_one_rels
420
- @has_one_rels ||= []
421
- end
422
-
423
- def self.belongs_to(relationship, opts={})
424
- belongs_to_rels[relationship] = opts
352
+
353
+ def self.references(relationship, opts={})
354
+ references_rels[relationship] = opts
425
355
 
426
356
  define_method(relationship) do
427
- create_or_get_proxy(BelongsToProxy, relationship).target
357
+ create_or_get_proxy(ReferencesProxy, relationship).target
428
358
  end
429
359
 
430
360
  define_method("#{relationship}=") do |new_target|
431
- create_or_get_proxy(BelongsToProxy, relationship).target = new_target
432
- write_derived_props(relationship) if @set_derived_props
361
+ create_or_get_proxy(ReferencesProxy, relationship).target = new_target
362
+ write_derived_props(relationship)
433
363
  end
434
364
 
435
365
  # Allows all writers to be invoked from the hash passed to initialize
436
366
  define_method("#{relationship}_id=") do |id|
437
- instance_variable_set("@#{relationship}_id".to_sym, id)
438
- write_derived_props(relationship) if @set_derived_props
367
+ @data["#{relationship}_id"] = id
368
+ write_derived_props(relationship)
439
369
  id
440
370
  end
441
371
 
442
372
  define_method("#{relationship}_id") do
443
- instance_variable_get("@#{relationship}_id")
373
+ @data["#{relationship}_id"]
444
374
  end
445
375
 
446
376
  create_validator(relationship, opts[:validator]) if opts[:validator]
@@ -449,39 +379,11 @@ module RelaxDB
449
379
  create_validation_msg(relationship, opts[:validation_msg]) if opts[:validation_msg]
450
380
  end
451
381
 
452
- class << self
453
- alias_method :references, :belongs_to
454
- end
455
-
456
- self.belongs_to_rels = {}
457
-
458
- def self.all_relationships
459
- belongs_to_rels + has_one_rels + has_many_rels + references_many_rels
460
- end
461
-
462
382
  def self.all params = {}
463
383
  AllDelegator.new self.name, params
464
384
  end
465
385
 
466
- # destroy! nullifies all relationships with peers and children before deleting
467
- # itself in CouchDB
468
- # The nullification and deletion are not performed in a transaction
469
- #
470
- # TODO: Current implemention may be inappropriate - causing CouchDB to try to JSON
471
- # encode undefined. Ensure nil is serialized? See has_many_spec#should nullify its child relationships
472
386
  def destroy!
473
- self.class.references_many_rels.each do |rel|
474
- send(rel).clear
475
- end
476
-
477
- self.class.has_many_rels.each do |rel|
478
- send(rel).clear
479
- end
480
-
481
- self.class.has_one_rels.each do |rel|
482
- send("#{rel}=".to_sym, nil)
483
- end
484
-
485
387
  # Implicitly prevent the object from being resaved by failing to update its revision
486
388
  RelaxDB.db.delete("#{_id}?rev=#{_rev}")
487
389
  self
@@ -523,15 +425,15 @@ module RelaxDB
523
425
  end
524
426
 
525
427
  #
526
- # Creates the corresponding view and stores it in CouchDB
428
+ # Creates the corresponding view, emitting the doc as the val
527
429
  # Adds by_ and paginate_by_ methods to the class
528
430
  #
529
- def self.view_by *atts
431
+ def self.view_docs_by *atts
530
432
  opts = atts.last.is_a?(Hash) ? atts.pop : {}
531
- __view_by_list__ << atts
433
+ __view_docs_by_list__ << atts
532
434
 
533
435
  if RelaxDB.create_views?
534
- ViewCreator.by_att_list([self.name], *atts).save
436
+ ViewCreator.docs_by_att_list([self.name], *atts).add_to_design_doc
535
437
  end
536
438
 
537
439
  by_name = "by_#{atts.join "_and_"}"
@@ -539,11 +441,11 @@ module RelaxDB
539
441
  define_method by_name do |*params|
540
442
  view_name = "#{self.name}_#{by_name}"
541
443
  if params.empty?
542
- res = RelaxDB.rf_view view_name, opts
444
+ RelaxDB.rf_view view_name, opts
543
445
  elsif params[0].is_a? Hash
544
- res = RelaxDB.rf_view view_name, opts.merge(params[0])
446
+ RelaxDB.rf_view view_name, opts.merge(params[0])
545
447
  else
546
- res = RelaxDB.rf_view(view_name, :key => params[0]).first
448
+ RelaxDB.rf_view(view_name, :key => params[0]).first
547
449
  end
548
450
  end
549
451
  end
@@ -559,12 +461,38 @@ module RelaxDB
559
461
  end
560
462
  end
561
463
 
464
+ #
465
+ # Creates the corresponding view, emitting 1 as the val
466
+ # Adds a by_ method to this class, but does not add a
467
+ # paginate method.
468
+ #
469
+ def self.view_by *atts
470
+ opts = atts.last.is_a?(Hash) ? atts.pop : {}
471
+ opts = opts.merge :reduce => false
472
+ __view_by_list__ << atts
473
+
474
+ if RelaxDB.create_views?
475
+ ViewCreator.by_att_list([self.name], *atts).add_to_design_doc
476
+ end
477
+
478
+ by_name = "by_#{atts.join "_and_"}"
479
+ meta_class.instance_eval do
480
+ define_method by_name do |*params|
481
+ view_name = "#{self.name}_#{by_name}"
482
+ if params.empty?
483
+ ViewByDelegator.new(view_name, opts)
484
+ elsif params[0].is_a? Hash
485
+ ViewByDelegator.new(view_name, opts.merge(params[0]))
486
+ else
487
+ ViewByDelegator.new(view_name, opts.merge(:key => params[0])).load!.first
488
+ end
489
+ end
490
+ end
491
+ end
492
+
562
493
  # Create a view allowing all instances of a particular class to be retreived
563
494
  def self.create_all_by_class_view
564
- if RelaxDB.create_views?
565
- view = ViewCreator.all
566
- view.save unless view.exists?
567
- end
495
+ ViewCreator.all.add_to_design_doc if RelaxDB.create_views?
568
496
  end
569
497
 
570
498
  def self.inherited subclass
@@ -588,10 +516,14 @@ module RelaxDB
588
516
  @hierarchy.uniq!
589
517
 
590
518
  if RelaxDB.create_views?
591
- ViewCreator.all(@hierarchy).save
592
- __view_by_list__.each do |atts|
593
- ViewCreator.by_att_list(@hierarchy, *atts).save
519
+ ViewCreator.all(@hierarchy).add_to_design_doc
520
+ __view_docs_by_list__.each do |atts|
521
+ ViewCreator.docs_by_att_list(@hierarchy, *atts).add_to_design_doc
594
522
  end
523
+
524
+ __view_by_list__.each do |atts|
525
+ ViewCreator.by_att_list(@hierarchy, *atts).add_to_design_doc
526
+ end
595
527
  end
596
528
  end
597
529