couchrest_model 2.1.0.rc1 → 2.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +15 -4
  4. data/Gemfile.activesupport-4.x +4 -0
  5. data/Gemfile.activesupport-5.x +4 -0
  6. data/README.md +2 -0
  7. data/VERSION +1 -1
  8. data/couchrest_model.gemspec +3 -2
  9. data/history.md +14 -1
  10. data/lib/couchrest/model/associations.rb +3 -8
  11. data/lib/couchrest/model/base.rb +15 -7
  12. data/lib/couchrest/model/casted_array.rb +22 -34
  13. data/lib/couchrest/model/configuration.rb +2 -0
  14. data/lib/couchrest/model/design.rb +4 -3
  15. data/lib/couchrest/model/designs/view.rb +37 -32
  16. data/lib/couchrest/model/dirty.rb +93 -19
  17. data/lib/couchrest/model/embeddable.rb +2 -14
  18. data/lib/couchrest/model/extended_attachments.rb +2 -4
  19. data/lib/couchrest/model/persistence.rb +14 -17
  20. data/lib/couchrest/model/properties.rb +46 -54
  21. data/lib/couchrest/model/property.rb +0 -3
  22. data/lib/couchrest/model/proxyable.rb +20 -4
  23. data/lib/couchrest/model/validations/uniqueness.rb +4 -1
  24. data/lib/couchrest_model.rb +2 -2
  25. data/spec/fixtures/models/article.rb +1 -1
  26. data/spec/fixtures/models/card.rb +2 -1
  27. data/spec/fixtures/models/person.rb +1 -0
  28. data/spec/fixtures/models/project.rb +3 -0
  29. data/spec/unit/assocations_spec.rb +73 -73
  30. data/spec/unit/attachment_spec.rb +34 -34
  31. data/spec/unit/base_spec.rb +102 -102
  32. data/spec/unit/casted_array_spec.rb +7 -7
  33. data/spec/unit/casted_spec.rb +7 -7
  34. data/spec/unit/configuration_spec.rb +11 -11
  35. data/spec/unit/connection_spec.rb +30 -30
  36. data/spec/unit/core_extensions/{time_parsing.rb → time_parsing_spec.rb} +21 -21
  37. data/spec/unit/design_spec.rb +38 -38
  38. data/spec/unit/designs/design_mapper_spec.rb +26 -26
  39. data/spec/unit/designs/migrations_spec.rb +13 -13
  40. data/spec/unit/designs/view_spec.rb +319 -274
  41. data/spec/unit/designs_spec.rb +39 -39
  42. data/spec/unit/dirty_spec.rb +188 -103
  43. data/spec/unit/embeddable_spec.rb +119 -117
  44. data/spec/unit/inherited_spec.rb +4 -4
  45. data/spec/unit/persistence_spec.rb +122 -122
  46. data/spec/unit/properties_spec.rb +466 -16
  47. data/spec/unit/property_protection_spec.rb +32 -32
  48. data/spec/unit/property_spec.rb +45 -436
  49. data/spec/unit/proxyable_spec.rb +140 -82
  50. data/spec/unit/subclass_spec.rb +14 -14
  51. data/spec/unit/translations_spec.rb +5 -5
  52. data/spec/unit/typecast_spec.rb +131 -131
  53. data/spec/unit/utils/migrate_spec.rb +2 -2
  54. data/spec/unit/validations_spec.rb +31 -31
  55. metadata +27 -12
  56. data/lib/couchrest/model/casted_hash.rb +0 -84
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9cebaec8e04dcd93d4d5f53740ba5ce5b6af068
4
- data.tar.gz: 71c3fb5e884c883124e05b2d69b60be3f474f7a1
3
+ metadata.gz: dc3de5182b5c551856bd3dbbbdea62fe97feccf8
4
+ data.tar.gz: 1aac104a38f10500bcfbf6d3812442b7fa6ca677
5
5
  SHA512:
6
- metadata.gz: 9cfc97e0e45f07cfadfb6d9bdfe89e7a1437e1ffbb935c6584d0f0d83ebb3fce064490330cc84452ff2237548358ca805e7bdf15d3b8d586b7800235297c91d9
7
- data.tar.gz: 7e0148b1299d685cb49af8c554ef5855b69e47cb270ed120b8ddebc5d2db2cd791f2a88de2689ca799d1b416b31f0886ba5c992b83056eeb496055c521e2dade
6
+ metadata.gz: f5c34608cb902751b5671abc660a23203cd7f340fcc05eefc670be5859f7693bb482bf05c400fd5789abfdd09affd9379ecc6589924b5ac70805a05fc0b81421
7
+ data.tar.gz: 39b5d65f53d8cb283f86fbfa0a3d6c27edea311dede32422cce4a0225cc42f98f2ed672cd65e01f48d176cc18a031f2758bbb12011f5391f7f57800ea88bcd80
data/.gitignore CHANGED
@@ -6,6 +6,6 @@ pkg
6
6
  .bundle
7
7
  couchdb.std*
8
8
  *.*~
9
- Gemfile.lock
9
+ *.lock
10
10
  spec.out
11
11
  *.gem
data/.travis.yml CHANGED
@@ -1,14 +1,25 @@
1
+ language: ruby
2
+ gemfile:
3
+ - Gemfile.activesupport-4.x
4
+ - Gemfile.activesupport-5.x
1
5
  rvm:
2
6
  - 2.3.0
3
- - 2.2.2
7
+ - 2.2.4
4
8
  - 2.1.10
5
9
  - 2.0.0
6
10
  - jruby
11
+ - rbx
7
12
  services: couchdb
8
- matrix:
9
- allow_failures:
10
- - rvm: jruby
11
13
  before_install:
12
14
  - gem install bundler
13
15
  env:
14
16
  - JRUBY_OPTS=--2.0
17
+ matrix:
18
+ allow_failures:
19
+ - rvm: 2.0.0
20
+ gemfile: Gemfile.activesupport-5.x
21
+ - rvm: 2.1.10
22
+ gemfile: Gemfile.activesupport-5.x
23
+ - rvm: jruby
24
+ gemfile: Gemfile.activesupport-5.x
25
+ - rvm: rbx # Has problems with Array#insert inheritence
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+
4
+ gem 'activesupport', '~> 4.0'
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+
4
+ gem 'activesupport', '~> 5.0'
data/README.md CHANGED
@@ -19,6 +19,8 @@ See the [update history](https://github.com/couchrest/couchrest_model/blob/maste
19
19
 
20
20
  ### Upgrading from an earlier version?
21
21
 
22
+ *Pre 2.2:* As of August 2016, dirty tracking has been radically re-factored away from ActiveModel::Dirty, which only has support for basic attributes, into a solution that uses [Hashdiff](https://github.com/liufengyun/hashdiff), more details available in the [pull request](https://github.com/couchrest/couchrest_model/pull/211). The result is that some of ActiveModel's Dirty methods are no longer available, these are: `changes_applied`, `restore_attributes`, `previous_changes`, and `changed_attributes`.
23
+
22
24
  *Pre 2.0:* As of June 2012, couchrest model no longer supports the `view_by` and `view` calls from the model. Views are no only accessed via a design document. If you have older code and wish to upgrade, please ensure you move to the new syntax for using views.
23
25
 
24
26
  *Pre 1.1:* As of April 2011 and the release of version 1.1.0, the default model type key is 'type' instead of 'couchrest-type'. Simply updating your project will not work unless you migrate your data or set the configuration option in your initializers:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.0.rc1
1
+ 2.2.0.beta1
@@ -23,9 +23,10 @@ Gem::Specification.new do |s|
23
23
  s.require_paths = ["lib"]
24
24
 
25
25
  s.add_dependency("couchrest", "2.0.0")
26
- s.add_dependency("activemodel", "~> 4.0")
26
+ s.add_dependency("activemodel", ">= 4.0.2")
27
27
  s.add_dependency("tzinfo", ">= 0.3.22")
28
- s.add_development_dependency("rspec", "~> 2.14.1")
28
+ s.add_dependency("hashdiff", "~> 0.3")
29
+ s.add_development_dependency("rspec", "~> 3.5.0")
29
30
  s.add_development_dependency("rack-test", ">= 0.5.7")
30
31
  s.add_development_dependency("rake", ">= 0.8.0")
31
32
  s.add_development_dependency("test-unit")
data/history.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # CouchRest Model Change History
2
2
 
3
- ## 2.1.0 - 2016-07-06
3
+ ## 2.2.0.beta1 - pending
4
+
5
+ * Radical re-factor of dirty tracking using Hashdiff gem, providing reliable change detection with nested data. ([PR](https://github.com/couchrest/couchrest_model/pull/211) @samlown)
6
+ * Implement proxy for factory methods ([PR](https://github.com/couchrest/couchrest_model/pull/210) @ellneal)
7
+ * Add view option to specify a custom emit value for views ([PR](https://github.com/couchrest/couchrest_model/pull/209) @ellneal)
8
+ * Support proxying to multiple databases ([PR](https://github.com/couchrest/couchrest_model/pull/206) @ellneal)
9
+ * Support concurrent auto design doc updates ([PR](https://github.com/couchrest/couchrest_model/pull/201) @ghempton)
10
+ * Upgrading to rspec 3.5 and adding ActiveSupport 5.X tests (@samlown)
11
+
12
+ ## 2.1.0 - skipped!
13
+
14
+ * No release, moved straight to 2.2.0.
15
+
16
+ ## 2.1.0.rc1 - 2016-07-06
4
17
 
5
18
  * Adding "persistent" connection configuration property (@samlown)
6
19
  * Releasing 2.1.0 off of CouchRest 2.0.0.
@@ -1,12 +1,8 @@
1
1
  module CouchRest
2
2
  module Model
3
+ # Basic support for relationships between CouchRest::Model::Base
3
4
  module Associations
4
-
5
- # Basic support for relationships between CouchRest::Model::Base
6
-
7
- def self.included(base)
8
- base.extend(ClassMethods)
9
- end
5
+ extend ActiveSupport::Concern
10
6
 
11
7
  module ClassMethods
12
8
 
@@ -240,8 +236,7 @@ module CouchRest
240
236
 
241
237
  # Override CastedArray instantiation_and_cast method for a simpler
242
238
  # version that will not try to cast the model.
243
- def instantiate_and_cast(obj, change = true)
244
- couchrest_parent_will_change! if change && use_dirty?
239
+ def instantiate_and_cast(obj)
245
240
  obj.casted_by = casted_by if obj.respond_to?(:casted_by)
246
241
  obj.casted_by_property = casted_by_property if obj.respond_to?(:casted_by_property)
247
242
  obj
@@ -45,19 +45,22 @@ module CouchRest
45
45
  #
46
46
  # Options supported:
47
47
  #
48
- # * :directly_set_attributes, true when data comes directly from database
48
+ # * :write_all_attributes, true when data comes directly from database so we can set protected and read-only attributes.
49
49
  # * :database, provide an alternative database
50
50
  #
51
51
  # If a block is provided the new model will be passed into the
52
52
  # block so that it can be populated.
53
- def initialize(attributes = {}, options = {})
53
+ def initialize(attributes = {}, options = {}, &block)
54
54
  super()
55
- prepare_all_attributes(attributes, options)
56
- # set the instance's database, if provided
55
+
56
+ # Always force the type of model
57
+ self[self.model_type_key] = self.class.model_type_value
58
+
59
+ # Some instances may require a different database
57
60
  self.database = options[:database] unless options[:database].nil?
58
- unless self['_id'] && self['_rev']
59
- self[self.model_type_key] = self.class.model_type_value
60
- end
61
+
62
+ # Deal with the attributes
63
+ write_attributes_for_initialization(attributes, options)
61
64
 
62
65
  yield self if block_given?
63
66
 
@@ -65,6 +68,11 @@ module CouchRest
65
68
  run_callbacks(:initialize) { self }
66
69
  end
67
70
 
71
+ def self.build(attrs = {}, options = {}, &block)
72
+
73
+
74
+ end
75
+
68
76
  alias :new_record? :new?
69
77
  alias :new_document? :new?
70
78
 
@@ -5,6 +5,7 @@
5
5
 
6
6
  module CouchRest::Model
7
7
  class CastedArray < Array
8
+ include CouchRest::Model::Configuration
8
9
  include CouchRest::Model::CastedBy
9
10
  include CouchRest::Model::Dirty
10
11
  attr_accessor :casted_by_property
@@ -29,43 +30,15 @@ module CouchRest::Model
29
30
  super(instantiate_and_cast(obj))
30
31
  end
31
32
 
32
- def []= index, obj
33
- value = instantiate_and_cast(obj, false)
34
- couchrest_parent_will_change! if use_dirty? && value != self[index]
35
- super(index, value)
33
+ def []=(index, obj)
34
+ super(index, instantiate_and_cast(obj))
36
35
  end
37
36
 
38
- def insert index, *args
39
- values = *args.map{|obj| instantiate_and_cast(obj, false)}
40
- couchrest_parent_will_change! if use_dirty?
37
+ def insert(index, *args)
38
+ values = args.map{|obj| instantiate_and_cast(obj)}
41
39
  super(index, *values)
42
40
  end
43
41
 
44
- def pop
45
- couchrest_parent_will_change! if use_dirty? && self.length > 0
46
- super
47
- end
48
-
49
- def shift
50
- couchrest_parent_will_change! if use_dirty? && self.length > 0
51
- super
52
- end
53
-
54
- def clear
55
- couchrest_parent_will_change! if use_dirty? && self.length > 0
56
- super
57
- end
58
-
59
- def delete(obj)
60
- couchrest_parent_will_change! if use_dirty? && self.length > 0
61
- super(obj)
62
- end
63
-
64
- def delete_at(index)
65
- couchrest_parent_will_change! if use_dirty? && self.length > 0
66
- super(index)
67
- end
68
-
69
42
  def build(*args)
70
43
  obj = casted_by_property.build(*args)
71
44
  self.push(obj)
@@ -76,11 +49,26 @@ module CouchRest::Model
76
49
  map{ |v| (v.respond_to?(:as_couch_json) ? v.as_couch_json : v)}
77
50
  end
78
51
 
52
+ # Overwrite the standard dirty tracking clearing.
53
+ # We don't have any properties, but we do need to check
54
+ # entries in our array.
55
+ def clear_changes_information
56
+ if use_dirty?
57
+ each do |val|
58
+ if val.respond_to?(:clear_changes_information)
59
+ val.clear_changes_information
60
+ end
61
+ end
62
+ @original_change_data = current_change_data
63
+ else
64
+ @original_change_data = nil
65
+ end
66
+ end
67
+
79
68
  protected
80
69
 
81
- def instantiate_and_cast(obj, change = true)
70
+ def instantiate_and_cast(obj)
82
71
  property = casted_by_property
83
- couchrest_parent_will_change! if change && use_dirty?
84
72
  if casted_by && property && obj.class != property.type
85
73
  property.cast_value(casted_by, obj)
86
74
  else
@@ -15,12 +15,14 @@ module CouchRest
15
15
  add_config :connection
16
16
  add_config :connection_config_file
17
17
  add_config :time_fraction_digits
18
+ add_config :disable_dirty_tracking
18
19
 
19
20
  configure do |config|
20
21
  config.model_type_key = 'type' # was 'couchrest-type'
21
22
  config.mass_assign_any_attribute = false
22
23
  config.auto_update_design_doc = true
23
24
  config.time_fraction_digits = 3
25
+ config.disable_dirty_tracking = false
24
26
 
25
27
  config.environment = :development
26
28
  config.connection_config_file = File.join(Dir.pwd, 'config', 'couchdb.yml')
@@ -17,6 +17,7 @@ module CouchRest
17
17
  def initialize(model, prefix = nil)
18
18
  self.model = model
19
19
  self.method_name = self.class.method_name(prefix)
20
+ @lock = Mutex.new
20
21
  suffix = prefix ? "_#{prefix}" : ''
21
22
  self["_id"] = "_design/#{model.to_s}#{suffix}"
22
23
  apply_defaults
@@ -26,7 +27,8 @@ module CouchRest
26
27
  if auto_update
27
28
  db ||= database
28
29
  if cache_checksum(db) != checksum
29
- sync!(db)
30
+ # Only allow one thread to update the design document at a time
31
+ @lock.synchronize { sync!(db) }
30
32
  set_cache_checksum(db, checksum)
31
33
  end
32
34
  end
@@ -39,6 +41,7 @@ module CouchRest
39
41
  # Load up the last copy. We never blindly overwrite the remote copy
40
42
  # as it may contain views that are not used or known about by
41
43
  # our model.
44
+
42
45
  doc = load_from_database(db)
43
46
 
44
47
  if !doc || doc['couchrest-hash'] != checksum
@@ -181,5 +184,3 @@ module CouchRest
181
184
  end
182
185
  end
183
186
  end
184
-
185
-
@@ -5,10 +5,10 @@ module CouchRest
5
5
  #
6
6
  # A proxy class that allows view queries to be created using
7
7
  # chained method calls. After each call a new instance of the method
8
- # is created based on the original in a similar fashion to ruby's Sequel
8
+ # is created based on the original in a similar fashion to ruby's Sequel
9
9
  # library, or Rails 3's Arel.
10
10
  #
11
- # CouchDB views have inherent limitations, so joins and filters as used in
11
+ # CouchDB views have inherent limitations, so joins and filters as used in
12
12
  # a normal relational database are not possible.
13
13
  #
14
14
  class View
@@ -52,7 +52,7 @@ module CouchRest
52
52
  # == View Execution Methods
53
53
  #
54
54
  # Request to the CouchDB database using the current query values.
55
-
55
+
56
56
  # Return each row wrapped in a ViewRow object. Unlike the raw
57
57
  # CouchDB request, this will provide an empty array if there
58
58
  # are no results.
@@ -65,7 +65,7 @@ module CouchRest
65
65
  else
66
66
  if execute && result['rows']
67
67
  @rows ||= result['rows'].map{|v| ViewRow.new(v, model, use_database)}
68
- else
68
+ else
69
69
  [ ]
70
70
  end
71
71
  end
@@ -81,7 +81,7 @@ module CouchRest
81
81
  end
82
82
 
83
83
  # Provide all the documents from the view. If the view has not been
84
- # prepared with the +include_docs+ option, each document will be
84
+ # prepared with the +include_docs+ option, each document will be
85
85
  # loaded individually.
86
86
  def docs
87
87
  if block_given?
@@ -93,17 +93,17 @@ module CouchRest
93
93
  end
94
94
  end
95
95
 
96
- # If another request has been made on the view, this will return
96
+ # If another request has been made on the view, this will return
97
97
  # the first document in the set. If not, a new query object will be
98
- # generated with a limit of 1 so that only the first document is
98
+ # generated with a limit of 1 so that only the first document is
99
99
  # loaded.
100
100
  def first
101
101
  result ? all.first : limit(1).all.first
102
102
  end
103
103
 
104
104
  # Same as first but will order the view in descending order. This
105
- # does not however reverse the search keys or the offset, so if you
106
- # are using a +startkey+ and +endkey+ you might end up with
105
+ # does not however reverse the search keys or the offset, so if you
106
+ # are using a +startkey+ and +endkey+ you might end up with
107
107
  # unexpected results.
108
108
  #
109
109
  # If in doubt, don't use this method!
@@ -127,7 +127,7 @@ module CouchRest
127
127
  #
128
128
  # Trying to use this method with the group option will raise an error.
129
129
  #
130
- # If no reduce function is defined, a query will be performed
130
+ # If no reduce function is defined, a query will be performed
131
131
  # to return the total number of rows, this is the equivalant of:
132
132
  #
133
133
  # view.limit(0).total_rows
@@ -142,7 +142,7 @@ module CouchRest
142
142
  end
143
143
  end
144
144
 
145
- # Check to see if the array of documents is empty. This *will*
145
+ # Check to see if the array of documents is empty. This *will*
146
146
  # perform the query and return all documents ready to use, if you don't
147
147
  # want to load anything, use +#total_rows+ or +#count+ instead.
148
148
  def empty?
@@ -181,7 +181,7 @@ module CouchRest
181
181
  #
182
182
  # In this example, the raw option will be ignored, and the total rows
183
183
  # will still be accessible.
184
- #
184
+ #
185
185
  def [](value)
186
186
  execute[value]
187
187
  end
@@ -194,8 +194,8 @@ module CouchRest
194
194
 
195
195
 
196
196
  # == View Filter Methods
197
- #
198
- # View filters return a copy of the view instance with the query
197
+ #
198
+ # View filters return a copy of the view instance with the query
199
199
  # modified appropriatly. Errors will be raised if the methods
200
200
  # are combined in an incorrect fashion.
201
201
  #
@@ -208,10 +208,10 @@ module CouchRest
208
208
  update_query(:key => value)
209
209
  end
210
210
 
211
- # Find all index keys that start with the value provided. May or may
211
+ # Find all index keys that start with the value provided. May or may
212
212
  # not be used in conjunction with the +endkey+ option.
213
213
  #
214
- # When the +#descending+ option is used (not the default), the start
214
+ # When the +#descending+ option is used (not the default), the start
215
215
  # and end keys should be reversed, as per the CouchDB API.
216
216
  #
217
217
  # Cannot be used if the key has been set.
@@ -220,7 +220,7 @@ module CouchRest
220
220
  update_query(:startkey => value)
221
221
  end
222
222
 
223
- # The result set should start from the position of the provided document.
223
+ # The result set should start from the position of the provided document.
224
224
  # The value may be provided as an object that responds to the +#id+ call
225
225
  # or a string.
226
226
  def startkey_doc(value)
@@ -237,19 +237,19 @@ module CouchRest
237
237
  update_query(:endkey => value)
238
238
  end
239
239
 
240
- # The result set should end at the position of the provided document.
241
- # The value may be provided as an object that responds to the +#id+
240
+ # The result set should end at the position of the provided document.
241
+ # The value may be provided as an object that responds to the +#id+
242
242
  # call or a string.
243
243
  def endkey_doc(value)
244
244
  update_query(:endkey_docid => value.is_a?(String) ? value : value.id)
245
245
  end
246
246
 
247
247
  # Keys is a special CouchDB option that will cause the view request to be POSTed
248
- # including an array of keys. Only documents with the matching keys will be
249
- # returned. This is much faster than sending multiple requests for a set
248
+ # including an array of keys. Only documents with the matching keys will be
249
+ # returned. This is much faster than sending multiple requests for a set
250
250
  # non-consecutive documents.
251
251
  #
252
- # If no values are provided, this method will act as a wrapper around
252
+ # If no values are provided, this method will act as a wrapper around
253
253
  # the rows result set, providing an array of keys.
254
254
  def keys(*keys)
255
255
  if keys.empty?
@@ -302,16 +302,16 @@ module CouchRest
302
302
  # Control whether the reduce function reduces to a set of distinct keys
303
303
  # or to a single result row.
304
304
  #
305
- # By default the value is false, and can only be set when the view's
305
+ # By default the value is false, and can only be set when the view's
306
306
  # +#reduce+ option has been set.
307
307
  def group
308
308
  raise "View#reduce must have been set before grouping is permitted" unless query[:reduce]
309
309
  update_query(:group => true)
310
310
  end
311
311
 
312
- # Will set the level the grouping should be performed to. As per the
312
+ # Will set the level the grouping should be performed to. As per the
313
313
  # CouchDB API, it only makes sense when the index key is an array.
314
- #
314
+ #
315
315
  # This will automatically set the group option.
316
316
  def group_level(value)
317
317
  group.update_query(:group_level => value.to_i)
@@ -325,7 +325,7 @@ module CouchRest
325
325
 
326
326
  # Allow the results of a query to be provided "stale". Setting to 'ok'
327
327
  # will disable all view updates for the query.
328
- # When 'update_after' is provided the index will be update after the
328
+ # When 'update_after' is provided the index will be update after the
329
329
  # result has been returned.
330
330
  def stale(value)
331
331
  unless (['ok', 'update_after'].include?(value.to_s))
@@ -445,17 +445,17 @@ module CouchRest
445
445
  create_model_methods(design_doc, name, opts)
446
446
  end
447
447
 
448
- # Simplified view definition. A new view will be added to the
448
+ # Simplified view definition. A new view will be added to the
449
449
  # provided design document using the name and options.
450
450
  #
451
- # If the view name starts with "by_" and +:by+ is not provided in
451
+ # If the view name starts with "by_" and +:by+ is not provided in
452
452
  # the options, the new view's map method will be interpreted and
453
453
  # generated automatically. For example:
454
454
  #
455
455
  # View.define(Meeting, design, "by_date_and_name")
456
456
  #
457
- # Will create a view that searches by the date and name properties.
458
- # Explicity setting the attributes to use is possible using the
457
+ # Will create a view that searches by the date and name properties.
458
+ # Explicity setting the attributes to use is possible using the
459
459
  # +:by+ option. For example:
460
460
  #
461
461
  # View.define(Meeting, design, "by_date_and_name", :by => [:date, :firstname, :lastname])
@@ -491,18 +491,23 @@ module CouchRest
491
491
  end
492
492
  raise "View cannot be created without recognised name, :map or :by options" if opts[:by].nil?
493
493
 
494
+ # convert emit symbols to properties
495
+ opts[:emit] = "doc['#{opts[:emit]}']" if opts[:emit].is_a?(Symbol)
496
+ opts[:emit] = "[" + opts[:emit].map { |i| i.is_a?(Symbol) ? "doc['#{i}']" : i }.join(', ') + "]" if opts[:emit].is_a?(Array)
497
+
494
498
  opts[:allow_blank] = opts[:allow_blank].nil? ? true : opts[:allow_blank]
495
499
  opts[:guards] ||= []
496
500
  opts[:guards].push "(doc['#{model.model_type_key}'] == '#{model.model_type_value}')"
497
501
 
498
502
  keys = opts[:by].map{|o| "doc['#{o}']"}
499
- emit = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
503
+ emit_keys = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
504
+ emit_value = opts[:emit] || 1;
500
505
  opts[:guards] += keys.map{|k| "(#{k} != null)"} unless opts[:allow_nil]
501
506
  opts[:guards] += keys.map{|k| "(#{k} != '')"} unless opts[:allow_blank]
502
507
  opts[:map] = <<-EOF
503
508
  function(doc) {
504
509
  if (#{opts[:guards].join(' && ')}) {
505
- emit(#{emit}, 1);
510
+ emit(#{emit_keys}, #{emit_value});
506
511
  }
507
512
  }
508
513
  EOF