duck_record 0.0.9.1 → 0.0.10

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