couchrest_model 2.0.3 → 2.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 38d13ee6807a1d8e08ec19b1402ec3e222c62731
4
- data.tar.gz: c5eec5eea342a0c58ca60df0e63f45239398fbfb
3
+ metadata.gz: dc12bf9934bec928c3317599950ad2ac7b57e623
4
+ data.tar.gz: 4244221435ea4d8dbd33a6c184ed97926ba5cf14
5
5
  SHA512:
6
- metadata.gz: 20fa41cff63d880fb8b681338935ffd946ff9bc32e2bb6de33a913d01e466c341708c80af011cf0c7785cd992e7d2963c7ebdce2c5ca625fe43b4e20360c7300
7
- data.tar.gz: 10af0f1dee17c7ca5098ccd0aab103686b1714ce95b0f3cccbbe9544faa716d024e11c9b579a04e4ae10594d5dcc5b4da9a45c819316fa3ea631ac034fc4bee8
6
+ metadata.gz: 337c15afdd8264dfd16341d50ac85a25f8651946f0b1994135e02ac118f63dbde6c7688c4a21e44e21eb639d85aa0f892d979b5465f3d9c516fd9141a9433756
7
+ data.tar.gz: 9200d9be82615a00e475f05947ccbcef682ab5867a23810368146db85064a63ac46f8f6453efd661a64c16f805376060d65dddcc58cae74ec7892637aeebcfe8
data/README.md CHANGED
@@ -87,6 +87,12 @@ require 'rails/test_unit/railtie'
87
87
 
88
88
  You'll then need to make sure any references to `config.active_record` are removed from your environment files.
89
89
 
90
+ or alternatively below command do the same work
91
+ ```ruby
92
+ rails new <application-name> --skip-active-record
93
+ ```
94
+ Now in the gem file just add [couchrest_model] and you are good to go.
95
+
90
96
  ## Generators
91
97
 
92
98
  ### Configuration
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.3
1
+ 2.0.4
@@ -1,5 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
1
  Gem::Specification.new do |s|
4
2
  s.name = %q{couchrest_model}
5
3
  s.version = `cat VERSION`.strip
@@ -24,7 +22,7 @@ Gem::Specification.new do |s|
24
22
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
23
  s.require_paths = ["lib"]
26
24
 
27
- s.add_dependency(%q<couchrest>, ">= 1.2")
25
+ s.add_dependency(%q<couchrest>, "~> 1.2.1")
28
26
  s.add_dependency(%q<mime-types>, ">= 1.16")
29
27
  s.add_dependency(%q<activemodel>, ">= 4.0", ">= 3.0")
30
28
  s.add_dependency(%q<tzinfo>, ">= 0.3.22")
data/history.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # CouchRest Model Change History
2
2
 
3
+ ## 2.0.4 - 2015-06-26
4
+
5
+ * Casting Array properties using anything that inherits from Hash. (@samlown)
6
+ * Adding `.proxy_method_names` call to help migrations with multiple proxied models. (@samlown)
7
+ * Fixing `collection_of` dirty tracking when setting with objects. (@samlown)
8
+ * Added support for design doc `#view_lib` method for [CommonJS modules in views](http://wiki.apache.org/couchdb/CommonJS_Modules). (@samlown)
9
+ * Updating CouchRest dependency to ~> 1.2.1 (@samlown)
10
+ * Migrations use design doc info requests as opposed to Stream (@samlown)
11
+
3
12
  ## 2.0.3 - 2014-07-04
4
13
 
5
14
  * Added find_by_view! method support for raising DocumentNotFound error when searching.
@@ -183,49 +183,57 @@ module CouchRest
183
183
  def initialize(array, property, parent)
184
184
  (array ||= []).compact!
185
185
  super(array, property, parent)
186
- casted_by[casted_by_property.to_s] = [] # replace the original array!
186
+ self.casted_by_attribute = [] # replace the original array!
187
187
  array.compact.each do |obj|
188
188
  check_obj(obj)
189
- casted_by[casted_by_property.to_s] << obj.id
189
+ casted_by_attribute << obj.id
190
190
  end
191
191
  end
192
192
 
193
193
  def << obj
194
194
  check_obj(obj)
195
- casted_by[casted_by_property.to_s] << obj.id
195
+ casted_by_attribute << obj.id
196
196
  super(obj)
197
197
  end
198
198
 
199
199
  def push(obj)
200
200
  check_obj(obj)
201
- casted_by[casted_by_property.to_s].push obj.id
201
+ casted_by_attribute.push obj.id
202
202
  super(obj)
203
203
  end
204
204
 
205
205
  def unshift(obj)
206
206
  check_obj(obj)
207
- casted_by[casted_by_property.to_s].unshift obj.id
207
+ casted_by_attribute.unshift obj.id
208
208
  super(obj)
209
209
  end
210
210
 
211
211
  def []= index, obj
212
212
  check_obj(obj)
213
- casted_by[casted_by_property.to_s][index] = obj.id
213
+ casted_by_attribute[index] = obj.id
214
214
  super(index, obj)
215
215
  end
216
216
 
217
217
  def pop
218
- casted_by[casted_by_property.to_s].pop
218
+ casted_by_attribute.pop
219
219
  super
220
220
  end
221
221
 
222
222
  def shift
223
- casted_by[casted_by_property.to_s].shift
223
+ casted_by_attribute.shift
224
224
  super
225
225
  end
226
226
 
227
227
  protected
228
228
 
229
+ def casted_by_attribute=(value)
230
+ casted_by.write_attribute(casted_by_property, value)
231
+ end
232
+
233
+ def casted_by_attribute
234
+ casted_by.read_attribute(casted_by_property)
235
+ end
236
+
229
237
  def check_obj(obj)
230
238
  raise "Object cannot be added to #{casted_by.class.to_s}##{casted_by_property.to_s} collection unless saved" if obj.new?
231
239
  end
@@ -10,6 +10,13 @@ module CouchRest
10
10
  # Times, unless provided with a time zone, are assumed to be in
11
11
  # UTC.
12
12
  #
13
+ # Uses String#to_r on seconds portion to avoid rounding errors. Eg:
14
+ # Time.parse_iso8601("2014-12-11T16:54:54.549Z").as_json
15
+ # => "2014-12-11T16:54:54.548Z"
16
+ #
17
+ # See: https://bugs.ruby-lang.org/issues/7829
18
+ #
19
+
13
20
  def parse_iso8601(string)
14
21
  if (string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2}(\.\d+)?)(Z| ?([\+|\s|\-])?(\d{2}):?(\d{2}))?/)
15
22
  # $1 = year
@@ -24,9 +31,9 @@ module CouchRest
24
31
  # $11 = tz difference minutes
25
32
 
26
33
  if $8 == 'Z' || $8.to_s.empty?
27
- utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_f)
34
+ utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_r)
28
35
  else
29
- new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_f, "#{$9 == '-' ? '-' : '+'}#{$10}:#{$11}")
36
+ new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_r, "#{$9 == '-' ? '-' : '+'}#{$10}:#{$11}")
30
37
  end
31
38
  else
32
39
  parse(string)
@@ -3,6 +3,7 @@ module CouchRest
3
3
  module Model
4
4
 
5
5
  class Design < ::CouchRest::Design
6
+ include ::CouchRest::Model::Designs::Migrations
6
7
 
7
8
  # The model Class that this design belongs to and method name
8
9
  attr_accessor :model, :method_name
@@ -56,108 +57,6 @@ module CouchRest
56
57
  self
57
58
  end
58
59
 
59
- # Migrate the design document preventing downtime on a production
60
- # system. Typically this will be used when auto updates are disabled.
61
- #
62
- # Steps taken are:
63
- #
64
- # 1. Compare the checksum with the current version
65
- # 2. If different, create a new design doc with timestamp
66
- # 3. Wait until the view returns a result
67
- # 4. Copy over the original design doc
68
- #
69
- # If a block is provided, it will be called with the result of the migration:
70
- #
71
- # * :no_change - Nothing performed as there are no changes.
72
- # * :created - Add a new design doc as non existed
73
- # * :migrated - Migrated the existing design doc.
74
- #
75
- # This can be used for progressivly printing the results of the migration.
76
- #
77
- # After completion, either a "cleanup" Proc object will be provided to finalize
78
- # the process and copy the document into place, or simply nil if no cleanup is
79
- # required. For example:
80
- #
81
- # print "Synchronising Cat model designs: "
82
- # callback = Cat.design_doc.migrate do |res|
83
- # puts res.to_s
84
- # end
85
- # if callback
86
- # puts "Cleaning up."
87
- # callback.call
88
- # end
89
- #
90
- def migrate(db = nil, &block)
91
- db ||= database
92
- doc = load_from_database(db)
93
- cleanup = nil
94
- id = self['_id']
95
-
96
- if !doc
97
- # no need to migrate, just save it
98
- new_doc = to_hash.dup
99
- db.save_doc(new_doc)
100
-
101
- result = :created
102
- elsif doc['couchrest-hash'] != checksum
103
- id += "_migration"
104
-
105
- # Delete current migration if there is one
106
- old_migration = load_from_database(db, id)
107
- db.delete_doc(old_migration) if old_migration
108
-
109
- # Save new design doc
110
- new_doc = doc.merge(to_hash)
111
- new_doc['_id'] = id
112
- new_doc.delete('_rev')
113
- db.save_doc(new_doc)
114
-
115
- # Proc definition to copy the migration doc over the original
116
- cleanup = Proc.new do
117
- db.copy_doc(new_doc, doc)
118
- db.delete_doc(new_doc)
119
- self
120
- end
121
-
122
- result = :migrated
123
- else
124
- # Already up to date
125
- result = :no_change
126
- end
127
-
128
- if new_doc && !new_doc['views'].empty?
129
- # Create a view query and send
130
- name = new_doc['views'].keys.first
131
- view = new_doc['views'][name]
132
- params = {:limit => 1}
133
- params[:reduce] = false if view['reduce']
134
- db.view("#{id}/_view/#{name}", params) do |res|
135
- # Block to use streamer!
136
- end
137
- end
138
-
139
- # Provide the result in block
140
- yield result if block_given?
141
-
142
- cleanup
143
- end
144
-
145
- # Perform a single migration and inmediatly request a cleanup operation:
146
- #
147
- # print "Synchronising Cat model designs: "
148
- # Cat.design_doc.migrate! do |res|
149
- # puts res.to_s
150
- # end
151
- #
152
- def migrate!(db = nil, &block)
153
- callback = migrate(db, &block)
154
- if callback.is_a?(Proc)
155
- callback.call
156
- else
157
- callback
158
- end
159
- end
160
-
161
60
  def checksum
162
61
  sum = self['couchrest-hash']
163
62
  if sum && (@_original_hash == to_hash)
@@ -209,6 +108,13 @@ module CouchRest
209
108
  filters[name.to_s] = function
210
109
  end
211
110
 
111
+ ######## VIEW LIBS #########
112
+
113
+ def create_view_lib(name, function)
114
+ filters = (self['views']['lib'] ||= {})
115
+ filters[name.to_s] = function
116
+ end
117
+
212
118
  protected
213
119
 
214
120
  def load_from_database(db = database, id = nil)
@@ -221,7 +127,7 @@ module CouchRest
221
127
  # Calculate and update the checksum of the Design document.
222
128
  # Used for ensuring the latest version has been sent to the database.
223
129
  #
224
- # This will generate an flatterned, ordered array of all the elements of the
130
+ # This will generate a flatterned, ordered array of all the elements of the
225
131
  # design document, convert to string then generate an MD5 Hash. This should
226
132
  # result in a consisitent Hash accross all platforms.
227
133
  #
@@ -233,14 +139,13 @@ module CouchRest
233
139
  base.delete('_id')
234
140
  base.delete('_rev')
235
141
  base.delete('couchrest-hash')
236
- result = nil
237
142
  flatten =
238
143
  lambda {|r|
239
144
  (recurse = lambda {|v|
240
145
  if v.is_a?(Hash) || v.is_a?(CouchRest::Document)
241
- v.to_a.map{|v| recurse.call(v)}.flatten
146
+ v.to_a.map{|p| recurse.call(p)}.flatten
242
147
  elsif v.is_a?(Array)
243
- v.flatten.map{|v| recurse.call(v)}
148
+ v.flatten.map{|p| recurse.call(p)}
244
149
  else
245
150
  v.to_s
246
151
  end
@@ -49,6 +49,11 @@ module CouchRest
49
49
  design_doc.create_filter(name, function)
50
50
  end
51
51
 
52
+ # Define a new view re-usable lib for shared functions.
53
+ def view_lib(name, function)
54
+ design_doc.create_view_lib(name, function)
55
+ end
56
+
52
57
  # Convenience wrapper to access model's type key option.
53
58
  def model_type_key
54
59
  model.model_type_key
@@ -0,0 +1,131 @@
1
+ module CouchRest
2
+ module Model
3
+ module Designs
4
+
5
+ # Design Document Migrations Support
6
+ #
7
+ # A series of methods used inside design documents in order to perform migrations.
8
+ #
9
+ module Migrations
10
+
11
+ # Migrate the design document preventing downtime on a production
12
+ # system. Typically this will be used when auto updates are disabled.
13
+ #
14
+ # Steps taken are:
15
+ #
16
+ # 1. Compare the checksum with the current version
17
+ # 2. If different, create a new design doc with timestamp
18
+ # 3. Wait until the view returns a result
19
+ # 4. Copy over the original design doc
20
+ #
21
+ # If a block is provided, it will be called with the result of the migration:
22
+ #
23
+ # * :no_change - Nothing performed as there are no changes.
24
+ # * :created - Add a new design doc as non existed
25
+ # * :migrated - Migrated the existing design doc.
26
+ #
27
+ # This can be used for progressivly printing the results of the migration.
28
+ #
29
+ # After completion, either a "cleanup" Proc object will be provided to finalize
30
+ # the process and copy the document into place, or simply nil if no cleanup is
31
+ # required. For example:
32
+ #
33
+ # print "Synchronising Cat model designs: "
34
+ # callback = Cat.design_doc.migrate do |res|
35
+ # puts res.to_s
36
+ # end
37
+ # if callback
38
+ # puts "Cleaning up."
39
+ # callback.call
40
+ # end
41
+ #
42
+ def migrate(db = nil, &block)
43
+ db ||= database
44
+ doc = load_from_database(db)
45
+ cleanup = nil
46
+ id = self['_id']
47
+
48
+ if !doc
49
+ # no need to migrate, just save it
50
+ new_doc = to_hash.dup
51
+ db.save_doc(new_doc)
52
+
53
+ result = :created
54
+ elsif doc['couchrest-hash'] != checksum
55
+ id += "_migration"
56
+
57
+ # Delete current migration if there is one
58
+ old_migration = load_from_database(db, id)
59
+ db.delete_doc(old_migration) if old_migration
60
+
61
+ # Save new design doc
62
+ new_doc = doc.merge(to_hash)
63
+ new_doc['_id'] = id
64
+ new_doc.delete('_rev')
65
+ db.save_doc(new_doc)
66
+
67
+ # Proc definition to copy the migration doc over the original
68
+ cleanup = Proc.new do
69
+ db.copy_doc(new_doc, doc)
70
+ db.delete_doc(new_doc)
71
+ self
72
+ end
73
+
74
+ result = :migrated
75
+ else
76
+ # Already up to date
77
+ result = :no_change
78
+ end
79
+
80
+ wait_for_view_update_completion(db, new_doc)
81
+
82
+ yield result if block_given?
83
+
84
+ cleanup
85
+ end
86
+
87
+ # Perform a single migration and inmediatly request a cleanup operation:
88
+ #
89
+ # print "Synchronising Cat model designs: "
90
+ # Cat.design_doc.migrate! do |res|
91
+ # puts res.to_s
92
+ # end
93
+ #
94
+ def migrate!(db = nil, &block)
95
+ callback = migrate(db, &block)
96
+ if callback.is_a?(Proc)
97
+ callback.call
98
+ else
99
+ callback
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def wait_for_view_update_completion(db, attrs)
106
+ if attrs && !attrs['views'].empty?
107
+ # Prepare a design doc we can use
108
+ doc = CouchRest::Design.new(attrs)
109
+ doc.database = db
110
+
111
+ # Request view, to trigger a *background* view update
112
+ doc.view(doc['views'].keys.first, :limit => 1, :stale => "update_after")
113
+
114
+ # Poll the view update process
115
+ while true
116
+ sleep 1
117
+ info = doc.info
118
+ if !info || !info['view_index']
119
+ raise "Migration error, unable to load design doc info: #{db.root}/#{doc.id}"
120
+ end
121
+ break if !info['view_index']['updater_running']
122
+ end
123
+ end
124
+ end
125
+
126
+
127
+ end
128
+
129
+ end
130
+ end
131
+ end
@@ -29,7 +29,7 @@ module CouchRest::Model
29
29
  if array
30
30
  if value.nil?
31
31
  value = []
32
- elsif [Hash, HashWithIndifferentAccess].include?(value.class)
32
+ elsif value.is_a?(Hash)
33
33
  # Assume provided as a params hash where key is index
34
34
  value = parameter_hash_to_array(value)
35
35
  elsif !value.is_a?(Array)
@@ -17,7 +17,8 @@ module CouchRest
17
17
  def proxy_for(assoc_name, options = {})
18
18
  db_method = options[:database_method] || "proxy_database"
19
19
  options[:class_name] ||= assoc_name.to_s.singularize.camelize
20
- proxied_model_names << options[:class_name] unless proxied_model_names.include?(options[:class_name])
20
+ proxy_method_names << assoc_name.to_sym unless proxy_method_names.include?(assoc_name.to_sym)
21
+ proxied_model_names << options[:class_name] unless proxied_model_names.include?(options[:class_name])
21
22
  class_eval <<-EOS, __FILE__, __LINE__ + 1
22
23
  def #{assoc_name}
23
24
  @#{assoc_name} ||= CouchRest::Model::Proxyable::ModelProxy.new(::#{options[:class_name]}, self, self.class.to_s.underscore, #{db_method})
@@ -48,6 +49,10 @@ module CouchRest
48
49
  @proxy_database_method
49
50
  end
50
51
 
52
+ def proxy_method_names
53
+ @proxy_method_names ||= []
54
+ end
55
+
51
56
  def proxied_model_names
52
57
  @proxied_model_names ||= []
53
58
  end
@@ -59,7 +59,7 @@ module CouchRest
59
59
  end
60
60
 
61
61
  def find_proxying_models
62
- CouchRest::Model::Base.subclasses.reject{|m| m.proxy_database_method.blank?}
62
+ CouchRest::Model::Base.subclasses.reject{|m| m.proxy_method_names.empty?}
63
63
  end
64
64
 
65
65
  def migrate_each_model(models, db = nil)
@@ -75,17 +75,22 @@ module CouchRest
75
75
  def migrate_each_proxying_model(models)
76
76
  callbacks = [ ]
77
77
  models.each do |model|
78
- submodels = model.proxied_model_names.map{|n| n.constantize}
79
- model.all.each do |base|
80
- puts "Finding proxied models for #{model}: \"#{base.send(model.proxy_database_method)}\""
81
- callbacks += migrate_each_model(submodels, base.proxy_database)
78
+ methods = model.proxy_method_names
79
+ methods.each do |method|
80
+ puts "Finding proxied models for #{model}##{method}"
81
+ model.all.each do |obj|
82
+ proxy = obj.send(method)
83
+ callbacks += migrate_each_model([proxy.model], proxy.database)
84
+ end
82
85
  end
83
86
  end
84
87
  callbacks
85
88
  end
86
89
 
87
90
  def migrate_design(model, design, db = nil)
88
- print "Migrating #{model.to_s}##{design.method_name}... "
91
+ print "Migrating #{model.to_s}##{design.method_name}"
92
+ print " on #{db.name}" if db
93
+ print "... "
89
94
  callback = design.migrate(db) do |result|
90
95
  puts "#{result.to_s.gsub(/_/, ' ')}"
91
96
  end
@@ -1,4 +1,4 @@
1
- require 'active_model'
1
+ require "active_model"
2
2
  require "active_model/callbacks"
3
3
  require "active_model/conversion"
4
4
  require "active_model/errors"
@@ -9,22 +9,22 @@ require "active_model/validator"
9
9
  require "active_model/validations"
10
10
  require "active_model/dirty"
11
11
 
12
- require 'active_support/core_ext'
13
- require 'active_support/json'
12
+ require "active_support/core_ext"
13
+ require "active_support/json"
14
14
 
15
- require 'mime/types'
15
+ require "mime/types"
16
16
  require "enumerator"
17
17
  require "time"
18
- require 'digest/md5'
18
+ require "digest/md5"
19
19
 
20
- require 'bigdecimal' # used in typecast
21
- require 'bigdecimal/util' # used in typecast
20
+ require "bigdecimal" # used in typecast
21
+ require "bigdecimal/util" # used in typecast
22
22
 
23
- require 'couchrest'
23
+ require "couchrest"
24
24
 
25
- require 'couchrest/model'
26
- require 'couchrest/model/errors'
27
- require 'couchrest/model/translation'
25
+ require "couchrest/model"
26
+ require "couchrest/model/errors"
27
+ require "couchrest/model/translation"
28
28
  require "couchrest/model/persistence"
29
29
  require "couchrest/model/typecast"
30
30
  require "couchrest/model/casted_by"
@@ -42,10 +42,11 @@ require "couchrest/model/proxyable"
42
42
  require "couchrest/model/associations"
43
43
  require "couchrest/model/configuration"
44
44
  require "couchrest/model/connection"
45
- require "couchrest/model/design"
46
- require "couchrest/model/designs"
45
+ require "couchrest/model/designs/migrations"
47
46
  require "couchrest/model/designs/design_mapper"
48
47
  require "couchrest/model/designs/view"
48
+ require "couchrest/model/design"
49
+ require "couchrest/model/designs"
49
50
 
50
51
  # Monkey patches applied to couchrest
51
52
  require "couchrest/model/support/couchrest_database"
@@ -62,6 +63,4 @@ require "couchrest/model/base"
62
63
  require "couchrest/model/utils/migrate.rb"
63
64
 
64
65
  # Add rails support *after* everything has loaded
65
- if defined?(Rails)
66
- require "couchrest/railtie"
67
- end
66
+ require "couchrest/railtie" if defined?(Rails)
@@ -126,6 +126,7 @@ describe "Assocations" do
126
126
  @invoice.entry_ids = @entries.collect{|i| i.id}
127
127
  @invoice.entries.length.should eql(3)
128
128
  @invoice.entries.first.should eql(@entries.first)
129
+ @invoice.changed?.should be_true
129
130
  end
130
131
 
131
132
  it "should ignore blank ids when set directly" do
@@ -171,6 +172,10 @@ describe "Assocations" do
171
172
 
172
173
  # Account for dirty tracking
173
174
  describe "dirty tracking" do
175
+ it "should register changes on replacement" do
176
+ @invoice.entries = @entries
177
+ @invoice.changed?.should be_true
178
+ end
174
179
  it "should register changes on push" do
175
180
  @invoice.changed?.should be_false
176
181
  @invoice.entries << @entries[0]
@@ -99,6 +99,21 @@ describe "Time Parsing core extension" do
99
99
  Time.parse_iso8601(txt).should eql(Time.new(2011, 04, 01, 19, 05, 30, "+02:30"))
100
100
  end
101
101
 
102
+ it "should parse JSON time with second fractions" do
103
+ txt = "2014-12-11T16:53:54.000Z"
104
+ Time.parse_iso8601(txt).should eql(Time.utc(2014, 12, 11, 16, 53, Rational(54000, 1000)))
105
+ end
106
+
107
+ it "should avoid rounding errors parsing JSON time with second fractions" do
108
+ txt = "2014-12-11T16:53:54.548Z"
109
+ Time.parse_iso8601(txt).should eql(Time.utc(2014, 12, 11, 16, 53, Rational(54548,1000)))
110
+ end
111
+
112
+ end
113
+
114
+ it "avoids seconds rounding error" do
115
+ time_string = "2014-12-11T16:54:54.549Z"
116
+ Time.parse_iso8601(time_string).as_json.should eql(time_string)
102
117
  end
103
118
 
104
119
  describe "resorting back to normal parse" do
@@ -163,84 +163,6 @@ describe CouchRest::Model::Design do
163
163
  end
164
164
 
165
165
 
166
- describe "#migrate" do
167
- # WARNING! ORDER IS IMPORTANT!
168
-
169
- describe "with limited changes" do
170
-
171
- class DesignSampleModelMigrate < DesignSampleModelBase
172
- end
173
-
174
- before :all do
175
- reset_test_db!
176
- @mod = DesignSampleModelMigrate
177
- @doc = @mod.design_doc
178
- @db = @mod.database
179
- end
180
-
181
- it "should create new design if non exists" do
182
- @db.should_receive(:view).with("#{@doc['_id']}/_view/#{@doc['views'].keys.first}", {:limit => 1, :reduce => false})
183
- callback = @doc.migrate do |res|
184
- res.should eql(:created)
185
- end
186
- doc = @db.get(@doc['_id'])
187
- doc['views']['all'].should eql(@doc['views']['all'])
188
- callback.should be_nil
189
- end
190
-
191
- it "should not change anything if design is up to date" do
192
- @doc.sync
193
- @db.should_not_receive(:view)
194
- callback = @doc.migrate do |res|
195
- res.should eql(:no_change)
196
- end
197
- callback.should be_nil
198
- end
199
-
200
- end
201
-
202
- describe "migrating a document if there are changes" do
203
-
204
- class DesignSampleModelMigrate2 < DesignSampleModelBase
205
- end
206
-
207
- before :all do
208
- reset_test_db!
209
- @mod = DesignSampleModelMigrate2
210
- @doc = @mod.design_doc
211
- @db = @mod.database
212
- @doc.sync!
213
- @doc.create_view(:by_name_and_surname)
214
- @doc_id = @doc['_id'] + '_migration'
215
- end
216
-
217
- it "should save new migration design doc" do
218
- @db.should_receive(:view).with("#{@doc_id}/_view/by_name", {:limit => 1, :reduce => false})
219
- @callback = @doc.migrate do |res|
220
- res.should eql(:migrated)
221
- end
222
- @callback.should_not be_nil
223
-
224
- # should not have updated original view until cleanup
225
- doc = @db.get(@doc['_id'])
226
- doc['views'].should_not have_key('by_name_and_surname')
227
-
228
- # Should have created the migration
229
- new_doc = @db.get(@doc_id)
230
- new_doc.should_not be_nil
231
-
232
- # should be possible to perform cleanup
233
- @callback.call
234
- lambda { new_doc = @db.get(@doc_id) }.should raise_error RestClient::ResourceNotFound
235
-
236
- doc = @db.get(@doc['_id'])
237
- doc['views'].should have_key('by_name_and_surname')
238
- end
239
-
240
- end
241
-
242
- end
243
-
244
166
 
245
167
  describe "#checksum" do
246
168
 
@@ -346,6 +268,17 @@ describe CouchRest::Model::Design do
346
268
  end
347
269
  end
348
270
 
271
+ describe "#create_view_lib" do
272
+ before :each do
273
+ @doc = DesignSampleModel.design_doc
274
+ end
275
+
276
+ it "should add simple view lib" do
277
+ @doc.create_view_lib('test', 'foobar')
278
+ @doc['views']['lib']['test'].should eql('foobar')
279
+ @doc['views']['lib'] = nil # cleanup
280
+ end
281
+ end
349
282
  end
350
283
 
351
284
 
@@ -120,5 +120,19 @@ describe CouchRest::Model::Designs::DesignMapper do
120
120
  end
121
121
  end
122
122
 
123
+ describe "#view_lib" do
124
+ before :each do
125
+ @object = @klass.new(DesignModel)
126
+ end
127
+
128
+ it "should add the #view_lib function to the design doc" do
129
+ val = "exports.bar = 42;"
130
+ @object.view_lib(:foo, val)
131
+ DesignModel.design_doc['views']['lib'].should_not be_empty
132
+ DesignModel.design_doc['views']['lib'].should_not be_blank
133
+ DesignModel.design_doc['views']['lib']['foo'].should eql(val)
134
+ end
135
+ end
136
+
123
137
  end
124
138
 
@@ -0,0 +1,104 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe CouchRest::Model::Designs::Migrations do
5
+
6
+ before :all do
7
+ reset_test_db!
8
+ end
9
+
10
+ describe "base methods" do
11
+
12
+ describe "#migrate" do
13
+ # WARNING! ORDER IS IMPORTANT!
14
+
15
+ describe "with limited changes" do
16
+
17
+ class MigrationModelBase < CouchRest::Model::Base
18
+ use_database DB
19
+ property :name
20
+ property :surname
21
+ design do
22
+ view :by_name
23
+ end
24
+ end
25
+
26
+ class DesignSampleModelMigrate < MigrationModelBase
27
+ end
28
+
29
+ before :all do
30
+ reset_test_db!
31
+ @mod = DesignSampleModelMigrate
32
+ @doc = @mod.design_doc
33
+ @db = @mod.database
34
+ end
35
+
36
+ it "should create new design if non exists" do
37
+ @db.should_receive(:view).with("#{@doc.name}/#{@doc['views'].keys.first}", {
38
+ :limit => 1, :stale => 'update_after', :reduce => false
39
+ })
40
+ callback = @doc.migrate do |res|
41
+ res.should eql(:created)
42
+ end
43
+ doc = @db.get(@doc['_id'])
44
+ doc['views']['all'].should eql(@doc['views']['all'])
45
+ callback.should be_nil
46
+ end
47
+
48
+ it "should not change anything if design is up to date" do
49
+ @doc.sync
50
+ @db.should_not_receive(:view)
51
+ callback = @doc.migrate do |res|
52
+ res.should eql(:no_change)
53
+ end
54
+ callback.should be_nil
55
+ end
56
+
57
+ end
58
+
59
+ describe "migrating a document if there are changes" do
60
+
61
+ class DesignSampleModelMigrate2 < MigrationModelBase
62
+ end
63
+
64
+ before :all do
65
+ reset_test_db!
66
+ @mod = DesignSampleModelMigrate2
67
+ @doc = @mod.design_doc
68
+ @db = @mod.database
69
+ @doc.sync!
70
+ @doc.create_view(:by_name_and_surname)
71
+ @doc_id = @doc['_id'] + '_migration'
72
+ end
73
+
74
+ it "should save new migration design doc" do
75
+ @db.should_receive(:view).with("#{@doc.name}_migration/by_name", {
76
+ :limit => 1, :reduce => false, :stale => 'update_after'
77
+ })
78
+ @callback = @doc.migrate do |res|
79
+ res.should eql(:migrated)
80
+ end
81
+ @callback.should_not be_nil
82
+
83
+ # should not have updated original view until cleanup
84
+ doc = @db.get(@doc['_id'])
85
+ doc['views'].should_not have_key('by_name_and_surname')
86
+
87
+ # Should have created the migration
88
+ new_doc = @db.get(@doc_id)
89
+ new_doc.should_not be_nil
90
+
91
+ # should be possible to perform cleanup
92
+ @callback.call
93
+ lambda { new_doc = @db.get(@doc_id) }.should raise_error RestClient::ResourceNotFound
94
+
95
+ doc = @db.get(@doc['_id'])
96
+ doc['views'].should have_key('by_name_and_surname')
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+ end
@@ -328,6 +328,15 @@ describe "properties of array of casted models" do
328
328
  @course.questions.last.class.should eql(Question)
329
329
  end
330
330
 
331
+ it "should allow attribute to be set from Hash subclass with ordered keys" do
332
+ ourhash = Class.new(HashWithIndifferentAccess)
333
+ hash = ourhash.new({ '0' => {:q => "Test1"}, '1' => {:q => 'Test2'} })
334
+ @course.questions = hash
335
+ @course.questions.length.should eql(2)
336
+ @course.questions.last.q.should eql('Test2')
337
+ @course.questions.last.class.should eql(Question)
338
+ end
339
+
331
340
  it "should raise an error if attempting to set single value for array type" do
332
341
  lambda {
333
342
  @course.questions = Question.new(:q => 'test1')
@@ -78,6 +78,11 @@ describe CouchRest::Model::Proxyable do
78
78
  @class.proxied_model_names.should eql(['Cat'])
79
79
  end
80
80
 
81
+ it "should add method names to proxied method name array" do
82
+ @class.proxy_for(:cats)
83
+ @class.proxy_method_names.should eql([:cats])
84
+ end
85
+
81
86
  it "should create a new method" do
82
87
  DummyProxyable.stub!(:method_defined?).and_return(true)
83
88
  DummyProxyable.proxy_for(:cats)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchrest_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - J. Chris Anderson
@@ -12,22 +12,22 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2014-07-04 00:00:00.000000000 Z
15
+ date: 2015-06-26 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: couchrest
19
19
  requirement: !ruby/object:Gem::Requirement
20
20
  requirements:
21
- - - ">="
21
+ - - "~>"
22
22
  - !ruby/object:Gem::Version
23
- version: '1.2'
23
+ version: 1.2.1
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
- - - ">="
28
+ - - "~>"
29
29
  - !ruby/object:Gem::Version
30
- version: '1.2'
30
+ version: 1.2.1
31
31
  - !ruby/object:Gem::Dependency
32
32
  name: mime-types
33
33
  requirement: !ruby/object:Gem::Requirement
@@ -184,6 +184,7 @@ files:
184
184
  - lib/couchrest/model/design.rb
185
185
  - lib/couchrest/model/designs.rb
186
186
  - lib/couchrest/model/designs/design_mapper.rb
187
+ - lib/couchrest/model/designs/migrations.rb
187
188
  - lib/couchrest/model/designs/view.rb
188
189
  - lib/couchrest/model/dirty.rb
189
190
  - lib/couchrest/model/document_queries.rb
@@ -253,6 +254,7 @@ files:
253
254
  - spec/unit/core_extensions/time_parsing.rb
254
255
  - spec/unit/design_spec.rb
255
256
  - spec/unit/designs/design_mapper_spec.rb
257
+ - spec/unit/designs/migrations_spec.rb
256
258
  - spec/unit/designs/view_spec.rb
257
259
  - spec/unit/designs_spec.rb
258
260
  - spec/unit/dirty_spec.rb
@@ -288,7 +290,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
288
290
  version: 1.3.1
289
291
  requirements: []
290
292
  rubyforge_project:
291
- rubygems_version: 2.2.2
293
+ rubygems_version: 2.4.6
292
294
  signing_key:
293
295
  specification_version: 4
294
296
  summary: Extends the CouchRest Document for advanced modelling.
@@ -334,6 +336,7 @@ test_files:
334
336
  - spec/unit/core_extensions/time_parsing.rb
335
337
  - spec/unit/design_spec.rb
336
338
  - spec/unit/designs/design_mapper_spec.rb
339
+ - spec/unit/designs/migrations_spec.rb
337
340
  - spec/unit/designs/view_spec.rb
338
341
  - spec/unit/designs_spec.rb
339
342
  - spec/unit/dirty_spec.rb