duck_record 0.0.7 → 0.0.8

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: 06bf2457884cf16768ed22c8eab54c06cd03e59d
4
- data.tar.gz: 2b9fdb3b1e20e91e52e2e553f061302f777d2885
3
+ metadata.gz: 26cdafb4677192d3b19b66b6c691f739e6dc70ae
4
+ data.tar.gz: 5c1e47c06bece7781a4812389dbc7218ae240ed1
5
5
  SHA512:
6
- metadata.gz: 42bfba1df7380612fa22a300225f8e75e46e14bccc565c7c1e403105d7b6bba293376e32e08a6a74fd7c47a21120c5a86ef836eabde8a23bac818eb7b80817b1
7
- data.tar.gz: '095d81f5ac4b851f3989f6bba870e0dd30cbf2c4542968183dc4e963fcc6a2162deb0956519ff9b9f1205acad6baa5fcb61f9de42db9de4f23d9935ead1ac449'
6
+ metadata.gz: ddf042c24da96f908e7dcb8183566574a7208e02da658e8cf023a8e729e63019a9a659c66a7f782ed8055842f7b45ac5fa74a690bd3d91b2e6901e428a474ee6
7
+ data.tar.gz: 6a63e7f449a1815fc742fc93530f90407645bb6d1b51ff5382dc4b6102e4dacd4eeb113b82a49c8125e972289a5ab8c856680dc0ceeabe9db927458c8e47a6ba
@@ -1,18 +1,29 @@
1
+ require 'active_support/core_ext/hash/keys'
1
2
  require 'active_model/forbidden_attributes_protection'
2
3
 
3
4
  module DuckRecord
4
5
  module AttributeAssignment
5
6
  extend ActiveSupport::Concern
6
- include ActiveModel::AttributeAssignment
7
+ include ActiveModel::ForbiddenAttributesProtection
7
8
 
8
9
  # Alias for ActiveModel::AttributeAssignment#assign_attributes. See ActiveModel::AttributeAssignment.
9
- def attributes=(attributes)
10
- assign_attributes(attributes)
10
+ def attributes=(attributes, force_write_readonly: false)
11
+ assign_attributes(attributes, force_write_readonly: force_write_readonly)
12
+ end
13
+
14
+ def assign_attributes(new_attributes, force_write_readonly: false)
15
+ unless new_attributes.respond_to?(:stringify_keys)
16
+ raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
17
+ end
18
+ return if new_attributes.nil? || new_attributes.empty?
19
+
20
+ attributes = new_attributes.stringify_keys
21
+ _assign_attributes(sanitize_for_mass_assignment(attributes), force_write_readonly: force_write_readonly)
11
22
  end
12
23
 
13
24
  private
14
25
 
15
- def _assign_attributes(attributes)
26
+ def _assign_attributes(attributes, force_write_readonly: false)
16
27
  multi_parameter_attributes = {}
17
28
  nested_parameter_attributes = {}
18
29
 
@@ -23,15 +34,23 @@ module DuckRecord
23
34
  nested_parameter_attributes[k] = attributes.delete(k)
24
35
  end
25
36
  end
26
- super(attributes)
27
37
 
28
- assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
29
- assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
38
+ attributes.each do |k, v|
39
+ _assign_attribute(k, v, force_write_readonly: force_write_readonly)
40
+ end
41
+
42
+ unless nested_parameter_attributes.empty?
43
+ assign_nested_parameter_attributes(nested_parameter_attributes, force_write_readonly: force_write_readonly)
44
+ end
45
+
46
+ unless multi_parameter_attributes.empty?
47
+ assign_multiparameter_attributes(multi_parameter_attributes, force_write_readonly: force_write_readonly)
48
+ end
30
49
  end
31
50
 
32
51
  # Assign any deferred nested attributes after the base attributes have been set.
33
- def assign_nested_parameter_attributes(pairs)
34
- pairs.each { |k, v| _assign_attribute(k, v) }
52
+ def assign_nested_parameter_attributes(pairs, force_write_readonly: false)
53
+ pairs.each { |k, v| _assign_attribute(k, v, force_write_readonly: force_write_readonly) }
35
54
  end
36
55
 
37
56
  # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
@@ -40,13 +59,14 @@ module DuckRecord
40
59
  # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
41
60
  # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
42
61
  # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
43
- def assign_multiparameter_attributes(pairs)
62
+ def assign_multiparameter_attributes(pairs, force_write_readonly: false)
44
63
  execute_callstack_for_multiparameter_attributes(
45
- extract_callstack_for_multiparameter_attributes(pairs)
64
+ extract_callstack_for_multiparameter_attributes(pairs),
65
+ force_write_readonly: force_write_readonly
46
66
  )
47
67
  end
48
68
 
49
- def execute_callstack_for_multiparameter_attributes(callstack)
69
+ def execute_callstack_for_multiparameter_attributes(callstack, force_write_readonly: false)
50
70
  errors = []
51
71
  callstack.each do |name, values_with_empty_parameters|
52
72
  begin
@@ -55,7 +75,7 @@ module DuckRecord
55
75
  else
56
76
  values = values_with_empty_parameters
57
77
  end
58
- send("#{name}=", values)
78
+ send("#{name}=", values, force_write_readonly: force_write_readonly)
59
79
  rescue => ex
60
80
  errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
61
81
  end
@@ -87,5 +107,13 @@ module DuckRecord
87
107
  def find_parameter_position(multiparameter_name)
88
108
  multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
89
109
  end
110
+
111
+ def _assign_attribute(k, v, force_write_readonly: false)
112
+ if respond_to?("#{k}=")
113
+ public_send("#{k}=", v, force_write_readonly: force_write_readonly)
114
+ else
115
+ raise UnknownAttributeError.new(self, k)
116
+ end
117
+ end
90
118
  end
91
119
  end
@@ -15,9 +15,9 @@ module DuckRecord
15
15
  DuckRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
16
16
 
17
17
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
18
- def __temp__#{safe_name}=(value)
18
+ def __temp__#{safe_name}=(value, force_write_readonly: false)
19
19
  name = ::DuckRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
20
- write_attribute(name, value)
20
+ write_attribute(name, value, force_write_readonly: force_write_readonly)
21
21
  end
22
22
  alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
23
23
  undef_method :__temp__#{safe_name}=
@@ -28,30 +28,32 @@ module DuckRecord
28
28
  # Updates the attribute identified by <tt>attr_name</tt> with the
29
29
  # specified +value+. Empty strings for Integer and Float columns are
30
30
  # turned into +nil+.
31
- def write_attribute(attr_name, value)
31
+ def write_attribute(attr_name, value, force_write_readonly: false)
32
32
  name = if self.class.attribute_alias?(attr_name)
33
- self.class.attribute_alias(attr_name).to_s
34
- else
35
- attr_name.to_s
36
- end
33
+ self.class.attribute_alias(attr_name).to_s
34
+ else
35
+ attr_name.to_s
36
+ end
37
37
 
38
- write_attribute_with_type_cast(name, value, true)
38
+ write_attribute_with_type_cast(name, value, true, force_write_readonly: force_write_readonly)
39
39
  end
40
40
 
41
- def raw_write_attribute(attr_name, value) # :nodoc:
42
- write_attribute_with_type_cast(attr_name, value, false)
41
+ def raw_write_attribute(attr_name, value, force_write_readonly: false) # :nodoc:
42
+ write_attribute_with_type_cast(attr_name, value, false, force_write_readonly: force_write_readonly)
43
43
  end
44
44
 
45
45
  private
46
46
 
47
47
  # Handle *= for method_missing.
48
- def attribute=(attribute_name, value)
49
- write_attribute(attribute_name, value)
48
+ def attribute=(attribute_name, value, force_write_readonly: false)
49
+ write_attribute(attribute_name, value, force_write_readonly: force_write_readonly)
50
50
  end
51
51
 
52
- def write_attribute_with_type_cast(attr_name, value, should_type_cast)
52
+ def write_attribute_with_type_cast(attr_name, value, should_type_cast, force_write_readonly: false)
53
53
  attr_name = attr_name.to_s
54
54
 
55
+ return if !force_write_readonly && self.class.readonly_attributes.include?(attr_name)
56
+
55
57
  if should_type_cast
56
58
  @attributes.write_from_user(attr_name, value)
57
59
  else
@@ -276,6 +276,7 @@ module DuckRecord #:nodoc:
276
276
  extend Translation
277
277
 
278
278
  include Core
279
+ include ReadonlyAttributes
279
280
  include ModelSchema
280
281
  include Inheritance
281
282
  include AttributeAssignment
@@ -66,7 +66,7 @@ module DuckRecord
66
66
  initialize_internals_callback
67
67
 
68
68
  if attributes
69
- assign_attributes(attributes)
69
+ assign_attributes(attributes, force_write_readonly: true)
70
70
  clear_changes_information
71
71
  end
72
72
 
@@ -0,0 +1,23 @@
1
+ module DuckRecord
2
+ module ReadonlyAttributes
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :_attr_readonly, instance_accessor: false
7
+ self._attr_readonly = []
8
+ end
9
+
10
+ module ClassMethods
11
+ # Attributes listed as readonly will be used to create a new record but update operations will
12
+ # ignore these fields.
13
+ def attr_readonly(*attributes)
14
+ self._attr_readonly = Set.new(attributes.map(&:to_s)) + (_attr_readonly || [])
15
+ end
16
+
17
+ # Returns an array of all the attributes that have been specified as readonly.
18
+ def readonly_attributes
19
+ _attr_readonly
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module DuckRecord
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
data/lib/duck_record.rb CHANGED
@@ -15,6 +15,7 @@ module DuckRecord
15
15
  autoload :Inheritance
16
16
  autoload :ModelSchema
17
17
  autoload :NestedAttributes
18
+ autoload :ReadonlyAttributes
18
19
  autoload :Reflection
19
20
  autoload :Serialization
20
21
  autoload :Translation
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duck_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - jasl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-17 00:00:00.000000000 Z
11
+ date: 2017-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -100,6 +100,7 @@ files:
100
100
  - lib/duck_record/model_schema.rb
101
101
  - lib/duck_record/nested_attributes.rb
102
102
  - lib/duck_record/nested_validate_association.rb
103
+ - lib/duck_record/readonly_attributes.rb
103
104
  - lib/duck_record/reflection.rb
104
105
  - lib/duck_record/serialization.rb
105
106
  - lib/duck_record/translation.rb