duck_record 0.0.9.1 → 0.0.10

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +13 -13
  3. data/lib/duck_record/associations/association.rb +15 -15
  4. data/lib/duck_record/associations/collection_association.rb +8 -8
  5. data/lib/duck_record/associations/collection_proxy.rb +1 -1
  6. data/lib/duck_record/associations/has_many_association.rb +0 -1
  7. data/lib/duck_record/associations/singular_association.rb +6 -6
  8. data/lib/duck_record/attribute/user_provided_default.rb +2 -2
  9. data/lib/duck_record/attribute.rb +70 -70
  10. data/lib/duck_record/attribute_assignment.rb +74 -74
  11. data/lib/duck_record/attribute_methods/before_type_cast.rb +9 -9
  12. data/lib/duck_record/attribute_methods/dirty.rb +29 -29
  13. data/lib/duck_record/attribute_methods/read.rb +27 -27
  14. data/lib/duck_record/attribute_methods/write.rb +20 -20
  15. data/lib/duck_record/attribute_methods.rb +8 -8
  16. data/lib/duck_record/attribute_mutation_tracker.rb +4 -4
  17. data/lib/duck_record/attribute_set/yaml_encoder.rb +5 -5
  18. data/lib/duck_record/attribute_set.rb +5 -5
  19. data/lib/duck_record/attributes.rb +14 -14
  20. data/lib/duck_record/base.rb +18 -18
  21. data/lib/duck_record/callbacks.rb +1 -1
  22. data/lib/duck_record/core.rb +35 -35
  23. data/lib/duck_record/inheritance.rb +25 -25
  24. data/lib/duck_record/model_schema.rb +10 -11
  25. data/lib/duck_record/nested_attributes.rb +149 -149
  26. data/lib/duck_record/reflection.rb +14 -14
  27. data/lib/duck_record/type/array.rb +6 -6
  28. data/lib/duck_record/type/registry.rb +21 -21
  29. data/lib/duck_record/type/serialized.rb +8 -8
  30. data/lib/duck_record/type/time.rb +0 -1
  31. data/lib/duck_record/type/unsigned_integer.rb +6 -6
  32. data/lib/duck_record/type.rb +16 -14
  33. data/lib/duck_record/validations/uniqueness_on_real_record.rb +45 -45
  34. data/lib/duck_record/validations.rb +6 -6
  35. data/lib/duck_record/version.rb +1 -1
  36. data/lib/duck_record.rb +7 -7
  37. metadata +7 -6
@@ -6,42 +6,42 @@ module DuckRecord
6
6
  module ClassMethods
7
7
  private
8
8
 
9
- # We want to generate the methods via module_eval rather than
10
- # define_method, because define_method is slower on dispatch.
11
- # Evaluating many similar methods may use more memory as the instruction
12
- # sequences are duplicated and cached (in MRI). define_method may
13
- # be slower on dispatch, but if you're careful about the closure
14
- # created, then define_method will consume much less memory.
15
- #
16
- # But sometimes the database might return columns with
17
- # characters that are not allowed in normal method names (like
18
- # 'my_column(omg)'. So to work around this we first define with
19
- # the __temp__ identifier, and then use alias method to rename
20
- # it to what we want.
21
- #
22
- # We are also defining a constant to hold the frozen string of
23
- # the attribute name. Using a constant means that we do not have
24
- # to allocate an object on each call to the attribute method.
25
- # Making it frozen means that it doesn't get duped when used to
26
- # key the @attributes in read_attribute.
27
- def define_method_attribute(name)
28
- safe_name = name.unpack('h*'.freeze).first
29
- temp_method = "__temp__#{safe_name}"
9
+ # We want to generate the methods via module_eval rather than
10
+ # define_method, because define_method is slower on dispatch.
11
+ # Evaluating many similar methods may use more memory as the instruction
12
+ # sequences are duplicated and cached (in MRI). define_method may
13
+ # be slower on dispatch, but if you're careful about the closure
14
+ # created, then define_method will consume much less memory.
15
+ #
16
+ # But sometimes the database might return columns with
17
+ # characters that are not allowed in normal method names (like
18
+ # 'my_column(omg)'. So to work around this we first define with
19
+ # the __temp__ identifier, and then use alias method to rename
20
+ # it to what we want.
21
+ #
22
+ # We are also defining a constant to hold the frozen string of
23
+ # the attribute name. Using a constant means that we do not have
24
+ # to allocate an object on each call to the attribute method.
25
+ # Making it frozen means that it doesn't get duped when used to
26
+ # key the @attributes in read_attribute.
27
+ def define_method_attribute(name)
28
+ safe_name = name.unpack("h*".freeze).first
29
+ temp_method = "__temp__#{safe_name}"
30
30
 
31
- DuckRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
31
+ DuckRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
32
32
 
33
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
33
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
34
34
  def #{temp_method}
35
35
  name = ::DuckRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
36
36
  _read_attribute(name) { |n| missing_attribute(n, caller) }
37
37
  end
38
38
  STR
39
39
 
40
- generated_attribute_methods.module_eval do
41
- alias_method name, temp_method
42
- undef_method temp_method
40
+ generated_attribute_methods.module_eval do
41
+ alias_method name, temp_method
42
+ undef_method temp_method
43
+ end
43
44
  end
44
- end
45
45
  end
46
46
 
47
47
  # Returns the value of the attribute identified by <tt>attr_name</tt> after
@@ -4,17 +4,17 @@ module DuckRecord
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- attribute_method_suffix '='
7
+ attribute_method_suffix "="
8
8
  end
9
9
 
10
10
  module ClassMethods
11
11
  private
12
12
 
13
- def define_method_attribute=(name)
14
- safe_name = name.unpack("h*".freeze).first
15
- DuckRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
13
+ def define_method_attribute=(name)
14
+ safe_name = name.unpack("h*".freeze).first
15
+ DuckRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
16
16
 
17
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
17
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
18
18
  def __temp__#{safe_name}=(value, force_write_readonly: false)
19
19
  name = ::DuckRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
20
20
  write_attribute(name, value, force_write_readonly: force_write_readonly)
@@ -22,7 +22,7 @@ module DuckRecord
22
22
  alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
23
23
  undef_method :__temp__#{safe_name}=
24
24
  STR
25
- end
25
+ end
26
26
  end
27
27
 
28
28
  # Updates the attribute identified by <tt>attr_name</tt> with the
@@ -44,24 +44,24 @@ module DuckRecord
44
44
 
45
45
  private
46
46
 
47
- # Handle *= for method_missing.
48
- def attribute=(attribute_name, value, force_write_readonly: false)
49
- write_attribute(attribute_name, value, force_write_readonly: force_write_readonly)
50
- end
47
+ # Handle *= for method_missing.
48
+ def attribute=(attribute_name, value, force_write_readonly: false)
49
+ write_attribute(attribute_name, value, force_write_readonly: force_write_readonly)
50
+ end
51
51
 
52
- def write_attribute_with_type_cast(attr_name, value, should_type_cast, force_write_readonly: false)
53
- attr_name = attr_name.to_s
52
+ def write_attribute_with_type_cast(attr_name, value, should_type_cast, force_write_readonly: false)
53
+ attr_name = attr_name.to_s
54
54
 
55
- return if !force_write_readonly && self.class.readonly_attributes.include?(attr_name)
55
+ return if !force_write_readonly && self.class.readonly_attributes.include?(attr_name)
56
56
 
57
- if should_type_cast
58
- @attributes.write_from_user(attr_name, value)
59
- else
60
- @attributes.write_cast_value(attr_name, value)
61
- end
57
+ if should_type_cast
58
+ @attributes.write_from_user(attr_name, value)
59
+ else
60
+ @attributes.write_cast_value(attr_name, value)
61
+ end
62
62
 
63
- value
64
- end
63
+ value
64
+ end
65
65
  end
66
66
  end
67
67
  end
@@ -1,7 +1,7 @@
1
- require 'active_support/core_ext/enumerable'
2
- require 'active_support/core_ext/string/filters'
3
- require 'mutex_m'
4
- require 'concurrent/map'
1
+ require "active_support/core_ext/enumerable"
2
+ require "active_support/core_ext/string/filters"
3
+ require "mutex_m"
4
+ require "concurrent/map"
5
5
 
6
6
  module DuckRecord
7
7
  # = Active Record Attribute Methods
@@ -317,9 +317,9 @@ module DuckRecord
317
317
 
318
318
  protected
319
319
 
320
- def attribute_method?(attr_name) # :nodoc:
321
- # We check defined? because Syck calls respond_to? before actually calling initialize.
322
- defined?(@attributes) && @attributes.key?(attr_name)
323
- end
320
+ def attribute_method?(attr_name) # :nodoc:
321
+ # We check defined? because Syck calls respond_to? before actually calling initialize.
322
+ defined?(@attributes) && @attributes.key?(attr_name)
323
+ end
324
324
  end
325
325
  end
@@ -64,13 +64,13 @@ module DuckRecord
64
64
  # Workaround for Ruby 2.2 "private attribute?" warning.
65
65
  protected
66
66
 
67
- attr_reader :attributes, :forced_changes
67
+ attr_reader :attributes, :forced_changes
68
68
 
69
69
  private
70
70
 
71
- def attr_names
72
- attributes.keys
73
- end
71
+ def attr_names
72
+ attributes.keys
73
+ end
74
74
  end
75
75
 
76
76
  class NullMutationTracker # :nodoc:
@@ -8,7 +8,7 @@ module DuckRecord
8
8
  end
9
9
 
10
10
  def encode(attribute_set, coder)
11
- coder['concise_attributes'] = attribute_set.each_value.map do |attr|
11
+ coder["concise_attributes"] = attribute_set.each_value.map do |attr|
12
12
  if attr.type.equal?(default_types[attr.name])
13
13
  attr.with_type(nil)
14
14
  else
@@ -18,10 +18,10 @@ module DuckRecord
18
18
  end
19
19
 
20
20
  def decode(coder)
21
- if coder['attributes']
22
- coder['attributes']
21
+ if coder["attributes"]
22
+ coder["attributes"]
23
23
  else
24
- attributes_hash = Hash[coder['concise_attributes'].map do |attr|
24
+ attributes_hash = Hash[coder["concise_attributes"].map do |attr|
25
25
  if attr.type.nil?
26
26
  attr = attr.with_type(default_types[attr.name])
27
27
  end
@@ -35,7 +35,7 @@ module DuckRecord
35
35
  # Workaround for Ruby 2.2 'private attribute?' warning.
36
36
  protected
37
37
 
38
- attr_reader :default_types
38
+ attr_reader :default_types
39
39
  end
40
40
  end
41
41
  end
@@ -1,4 +1,4 @@
1
- require 'duck_record/attribute_set/yaml_encoder'
1
+ require "duck_record/attribute_set/yaml_encoder"
2
2
 
3
3
  module DuckRecord
4
4
  class AttributeSet # :nodoc:
@@ -87,12 +87,12 @@ module DuckRecord
87
87
  # Workaround for Ruby 2.2 "private attribute?" warning.
88
88
  protected
89
89
 
90
- attr_reader :attributes
90
+ attr_reader :attributes
91
91
 
92
92
  private
93
93
 
94
- def initialized_attributes
95
- attributes.select { |_, attr| attr.initialized? }
96
- end
94
+ def initialized_attributes
95
+ attributes.select { |_, attr| attr.initialized? }
96
+ end
97
97
  end
98
98
  end
@@ -240,22 +240,22 @@ module DuckRecord
240
240
 
241
241
  private
242
242
 
243
- NO_DEFAULT_PROVIDED = Object.new # :nodoc:
244
- private_constant :NO_DEFAULT_PROVIDED
243
+ NO_DEFAULT_PROVIDED = Object.new # :nodoc:
244
+ private_constant :NO_DEFAULT_PROVIDED
245
245
 
246
- def define_default_attribute(name, value, type)
247
- if value == NO_DEFAULT_PROVIDED
248
- default_attribute = _default_attributes[name].with_type(type)
249
- else
250
- default_attribute = Attribute::UserProvidedDefault.new(
251
- name,
252
- value,
253
- type,
254
- _default_attributes.fetch(name.to_s) { nil },
255
- )
246
+ def define_default_attribute(name, value, type)
247
+ if value == NO_DEFAULT_PROVIDED
248
+ default_attribute = _default_attributes[name].with_type(type)
249
+ else
250
+ default_attribute = Attribute::UserProvidedDefault.new(
251
+ name,
252
+ value,
253
+ type,
254
+ _default_attributes.fetch(name.to_s) { nil },
255
+ )
256
+ end
257
+ _default_attributes[name] = default_attribute
256
258
  end
257
- _default_attributes[name] = default_attribute
258
- end
259
259
  end
260
260
  end
261
261
  end
@@ -1,21 +1,21 @@
1
- require 'yaml'
2
- require 'active_support/benchmarkable'
3
- require 'active_support/dependencies'
4
- require 'active_support/descendants_tracker'
5
- require 'active_support/time'
6
- require 'active_support/core_ext/module/attribute_accessors'
7
- require 'active_support/core_ext/array/extract_options'
8
- require 'active_support/core_ext/hash/deep_merge'
9
- require 'active_support/core_ext/hash/slice'
10
- require 'active_support/core_ext/hash/transform_values'
11
- require 'active_support/core_ext/string/behavior'
12
- require 'active_support/core_ext/kernel/singleton_class'
13
- require 'active_support/core_ext/module/introspection'
14
- require 'active_support/core_ext/object/duplicable'
15
- require 'active_support/core_ext/class/subclasses'
16
- require 'duck_record/define_callbacks'
17
- require 'duck_record/errors'
18
- require 'duck_record/attributes'
1
+ require "yaml"
2
+ require "active_support/benchmarkable"
3
+ require "active_support/dependencies"
4
+ require "active_support/descendants_tracker"
5
+ require "active_support/time"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/core_ext/array/extract_options"
8
+ require "active_support/core_ext/hash/deep_merge"
9
+ require "active_support/core_ext/hash/slice"
10
+ require "active_support/core_ext/hash/transform_values"
11
+ require "active_support/core_ext/string/behavior"
12
+ require "active_support/core_ext/kernel/singleton_class"
13
+ require "active_support/core_ext/module/introspection"
14
+ require "active_support/core_ext/object/duplicable"
15
+ require "active_support/core_ext/class/subclasses"
16
+ require "duck_record/define_callbacks"
17
+ require "duck_record/errors"
18
+ require "duck_record/attributes"
19
19
 
20
20
  module DuckRecord #:nodoc:
21
21
  # = Active Record
@@ -318,7 +318,7 @@ module DuckRecord
318
318
  included do
319
319
  include ActiveModel::Validations::Callbacks
320
320
 
321
- define_model_callbacks :initialize, :only => :after
321
+ define_model_callbacks :initialize, only: :after
322
322
  end
323
323
  end
324
324
  end
@@ -1,7 +1,7 @@
1
- require 'thread'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
- require 'active_support/core_ext/object/duplicable'
4
- require 'active_support/core_ext/string/filters'
1
+ require "thread"
2
+ require "active_support/core_ext/hash/indifferent_access"
3
+ require "active_support/core_ext/object/duplicable"
4
+ require "active_support/core_ext/string/filters"
5
5
 
6
6
  module DuckRecord
7
7
  module Core
@@ -151,7 +151,7 @@ module DuckRecord
151
151
  # coder # => {"attributes" => {"id" => nil, ... }}
152
152
  def encode_with(coder)
153
153
  self.class.yaml_encoder.encode(@attributes, coder)
154
- coder['duck_record_yaml_version'] = 2
154
+ coder["duck_record_yaml_version"] = 2
155
155
  end
156
156
 
157
157
  # Clone and freeze the attributes hash such that associations are still
@@ -187,9 +187,9 @@ module DuckRecord
187
187
  if has_attribute?(name)
188
188
  "#{name}: #{attribute_for_inspect(name)}"
189
189
  end
190
- end.compact.join(', ')
190
+ end.compact.join(", ")
191
191
  else
192
- 'not initialized'
192
+ "not initialized"
193
193
  end
194
194
 
195
195
  "#<#{self.class} #{inspection}>"
@@ -201,19 +201,19 @@ module DuckRecord
201
201
  return super if custom_inspect_method_defined?
202
202
  pp.object_address_group(self) do
203
203
  if defined?(@attributes) && @attributes
204
- pp.seplist(self.class.attribute_names, proc { pp.text ',' }) do |attribute_name|
204
+ pp.seplist(self.class.attribute_names, proc { pp.text "," }) do |attribute_name|
205
205
  attribute_value = read_attribute(attribute_name)
206
- pp.breakable ' '
206
+ pp.breakable " "
207
207
  pp.group(1) do
208
208
  pp.text attribute_name
209
- pp.text ':'
209
+ pp.text ":"
210
210
  pp.breakable
211
211
  pp.pp attribute_value
212
212
  end
213
213
  end
214
214
  else
215
- pp.breakable ' '
216
- pp.text 'not initialized'
215
+ pp.breakable " "
216
+ pp.text "not initialized"
217
217
  end
218
218
  end
219
219
  end
@@ -225,33 +225,33 @@ module DuckRecord
225
225
 
226
226
  private
227
227
 
228
- # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
229
- # the array, and then rescues from the possible +NoMethodError+. If those elements are
230
- # +DuckRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
231
- # which significantly impacts upon performance.
232
- #
233
- # So we can avoid the +method_missing+ hit by explicitly defining +#to_ary+ as +nil+ here.
234
- #
235
- # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
236
- def to_ary
237
- nil
238
- end
228
+ # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
229
+ # the array, and then rescues from the possible +NoMethodError+. If those elements are
230
+ # +DuckRecord::Base+'s, then this triggers the various +method_missing+'s that we have,
231
+ # which significantly impacts upon performance.
232
+ #
233
+ # So we can avoid the +method_missing+ hit by explicitly defining +#to_ary+ as +nil+ here.
234
+ #
235
+ # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
236
+ def to_ary
237
+ nil
238
+ end
239
239
 
240
- def init_internals
241
- @readonly = false
242
- end
240
+ def init_internals
241
+ @readonly = false
242
+ end
243
243
 
244
- def initialize_internals_callback
245
- end
244
+ def initialize_internals_callback
245
+ end
246
246
 
247
- def thaw
248
- if frozen?
249
- @attributes = @attributes.dup
247
+ def thaw
248
+ if frozen?
249
+ @attributes = @attributes.dup
250
+ end
250
251
  end
251
- end
252
252
 
253
- def custom_inspect_method_defined?
254
- self.class.instance_method(:inspect).owner != DuckRecord::Base.instance_method(:inspect).owner
255
- end
253
+ def custom_inspect_method_defined?
254
+ self.class.instance_method(:inspect).owner != DuckRecord::Base.instance_method(:inspect).owner
255
+ end
256
256
  end
257
257
  end
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/hash/indifferent_access'
1
+ require "active_support/core_ext/hash/indifferent_access"
2
2
 
3
3
  module DuckRecord
4
4
  # == Single table inheritance
@@ -96,35 +96,35 @@ module DuckRecord
96
96
 
97
97
  protected
98
98
 
99
- # Returns the class type of the record using the current module as a prefix. So descendants of
100
- # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
101
- def compute_type(type_name)
102
- if type_name.start_with?('::'.freeze)
103
- # If the type is prefixed with a scope operator then we assume that
104
- # the type_name is an absolute reference.
105
- ActiveSupport::Dependencies.constantize(type_name)
106
- else
107
- type_candidate = @_type_candidates_cache[type_name]
108
- if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate)
109
- return type_constant
110
- end
99
+ # Returns the class type of the record using the current module as a prefix. So descendants of
100
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
101
+ def compute_type(type_name)
102
+ if type_name.start_with?("::".freeze)
103
+ # If the type is prefixed with a scope operator then we assume that
104
+ # the type_name is an absolute reference.
105
+ ActiveSupport::Dependencies.constantize(type_name)
106
+ else
107
+ type_candidate = @_type_candidates_cache[type_name]
108
+ if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate)
109
+ return type_constant
110
+ end
111
111
 
112
- # Build a list of candidates to search for
113
- candidates = []
114
- name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
115
- candidates << type_name
112
+ # Build a list of candidates to search for
113
+ candidates = []
114
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
115
+ candidates << type_name
116
116
 
117
- candidates.each do |candidate|
118
- constant = ActiveSupport::Dependencies.safe_constantize(candidate)
119
- if candidate == constant.to_s
120
- @_type_candidates_cache[type_name] = candidate
121
- return constant
117
+ candidates.each do |candidate|
118
+ constant = ActiveSupport::Dependencies.safe_constantize(candidate)
119
+ if candidate == constant.to_s
120
+ @_type_candidates_cache[type_name] = candidate
121
+ return constant
122
+ end
122
123
  end
123
- end
124
124
 
125
- raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
125
+ raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
126
+ end
126
127
  end
127
- end
128
128
  end
129
129
  end
130
130
  end
@@ -7,7 +7,6 @@ module DuckRecord
7
7
  end
8
8
 
9
9
  module ClassMethods
10
-
11
10
  def attribute_types # :nodoc:
12
11
  load_schema
13
12
  @attribute_types ||= Hash.new
@@ -42,19 +41,19 @@ module DuckRecord
42
41
 
43
42
  private
44
43
 
45
- def schema_loaded?
46
- defined?(@loaded) && @loaded
47
- end
44
+ def schema_loaded?
45
+ defined?(@loaded) && @loaded
46
+ end
48
47
 
49
- def load_schema
50
- unless schema_loaded?
51
- load_schema!
48
+ def load_schema
49
+ unless schema_loaded?
50
+ load_schema!
51
+ end
52
52
  end
53
- end
54
53
 
55
- def load_schema!
56
- @loaded = true
57
- end
54
+ def load_schema!
55
+ @loaded = true
56
+ end
58
57
  end
59
58
  end
60
59
  end