couchrest_model 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
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