Module: ActiveJsonModel::Array

Defined in:
lib/active_json_model/array.rb

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base_class) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/active_json_model/array.rb', line 16

def self.included(base_class)
  # Add all the class methods to the included class
  base_class.extend(ClassMethods)

  # Add additional settings into the class
  base_class.class_eval do
    # Make sure the objects will be ActiveModels
    include ::ActiveModel::Model unless include?(::ActiveModel::Model)

    # This class will be have like a list-like object
    include ::Enumerable unless include?(::Enumerable)

    # Make sure that it has dirty tracking
    include ::ActiveModel::Dirty unless include?(::ActiveModel::Dirty)

    # The raw values for the
    attr_accessor :values

    # Most of the functionality gets delegated to the actual values array. This is almost all possible methods for
    # array, leaving off those that might be problems for equality checking, etc.
    delegate :try_convert, :&, :*, :+, :-, :<<, :<=>, :[], :[]=, :all?, :any?, :append, :assoc, :at, :bsearch,
             :bsearch_index, :clear, :collect, :collect!, :combination, :compact, :compact!, :concat, :count,
             :cycle, :deconstruct, :delete, :delete_at, :delete_if, :difference, :dig, :drop, :drop_while,
             :each, :each_index, :empty?, :eql?, :fetch, :fill, :filter!, :find_index, :first, :flatten,
             :flatten!, :hash, :include?, :index, :initialize_copy, :insert, :inspect, :intersection, :join,
             :keep_if, :last, :length, :map, :map!, :max, :min, :minmax, :none?, :old_to_s, :one?, :pack,
             :permutation, :pop, :prepend, :product, :push, :rassoc, :reject, :reject!, :repeated_combination,
             :repeated_permutation, :replace, :reverse, :reverse!, :reverse_each, :rindex, :rotate, :rotate!,
             :sample, :select!, :shift, :shuffle, :shuffle!, :size, :slice, :slice!, :sort, :sort!,
             :sort_by!, :sum, :take, :take_while, :transpose, :union, :uniq, :uniq!, :unshift, :values_at, :zip, :|,
             to: :values

    # Has this model changed? Override's <code>ActiveModel::Dirty</code>'s base behavior to properly handle
    # recursive changes.
    #
    # @return [Boolean] true if any attribute has changed, false otherwise
    def changed?
      # Note: this method is implemented here versus in the module overall because if it is implemented in the
      # module overall, it doesn't properly override the implementation for <code>ActiveModel::Dirty</code> that
      # gets dynamically pulled in using the <code>included</code> hook.
      super || values != @_active_json_model_original_values || values&.any?{|val| val&.respond_to?(:changed?) && val.changed? }
    end

    # For new/loaded tracking
    @_active_json_model_dumped = false
    @_active_json_model_loaded = false

    # Register model validation to handle recursive validation into the model tree
    validate :active_json_model_validate

    def initialize(arr=nil, **kwargs)
      if !arr.nil? && !kwargs[:values].nil?
        raise ArgumentError.new('Can only specify either array or values for ActiveJsonModel::Array')
      end

      # Just repackage as named parameters
      kwargs[:values] = arr unless arr.nil?

      unless kwargs.key?(:values)
        kwargs[:values] = []
      end

      # Invoke the superclass constructor to let active model do the work of setting the attributes
      super(**kwargs).tap do |_|
        # Clear out any recorded changes as this object is starting fresh
        clear_changes_information
        @_active_json_model_original_values = self.values
      end
    end

    # Select certain values based on a condition and generate a new ActiveJsonModel Array
    # @return [ActiveJsonModel] the filtered array
    def select(&block)
      if block
        self.class.new(values: values.select(&block))
      else
        values.select
      end
    end

    # As in the real implementation, <code>filter</code> is just <code>select</code>
    alias_method :filter, :select
  end
end

Instance Method Details

#active_json_model_validateObject

Validate method that handles recursive validation into models in the array. Individual validations on attributes for this model will be handled by the standard mechanism.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/active_json_model/array.rb', line 195

def active_json_model_validate
  errors.add(:values, 'ActiveJsonModel::Array values must be an array') unless values.is_a?(::Array)

  values.each_with_index do |val, i|
    # Check if attribute value is an ActiveJsonModel
    if val && val.respond_to?(:valid?)
      # This call to <code>valid?</code> is important because it will actually trigger recursive validations
      unless val.valid?
        val.errors.each do |error|
          errors.add("[#{i}].#{error.attribute}".to_sym, error.message)
        end
      end
    end

    if self.class.active_json_model_array_serialization_tuple.validate_proc

      # It's a proc (likely lambda)
      if self.class.active_json_model_array_serialization_tuple.validate_proc.arity == 4
        # Handle the validator_for_item_type validators that need to take the self as a param
        # for recursive validators
        self.class.active_json_model_array_serialization_tuple.validate_proc.call(val, i, errors, self)
      else
        self.class.active_json_model_array_serialization_tuple.validate_proc.call(val, i, errors)
      end

    elsif self.class.active_json_model_array_serialization_tuple.validate_method

      # It's implemented as method on this object
      send(self.class.active_json_model_array_serialization_tuple.validate_method, val, i)
    end
  end
end

#dump_to_jsonObject



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/active_json_model/array.rb', line 170

def dump_to_json
  # Record that the data has been dumped
  @_active_json_model_dumped = true

  unless self.class.active_json_model_array_serialization_tuple
    raise RuntimeError.new('ActiveJsonModel::Array not properly configured')
  end

  return nil if values.nil?

  values.map do |val|
    if self.class.active_json_model_array_serialization_tuple.serialize_proc
      self.class.active_json_model_array_serialization_tuple.serialize_proc.call(val)
    else
      send(self.class.active_json_model_array_serialization_tuple.deserialize_method, val)
    end
  end.tap do |vals|
    # All changes are cleared after dump
    clear_changes_information
    @_active_json_model_original_values = vals
  end
end

#dumped?Boolean

Was this instance dumped to JSON?

Returns:

  • (Boolean)

    true if dumped to JSON, false otherwise



109
110
111
# File 'lib/active_json_model/array.rb', line 109

def dumped?
  @_active_json_model_dumped
end

#load_from_json(json_array) ⇒ Object

Load array for this instance from a JSON array

Parameters:

  • json_array (Array)

    array of data to be loaded into a model instance



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/active_json_model/array.rb', line 128

def load_from_json(json_array)
  # Record this object was loaded
  @_active_json_model_loaded = true

  if json_array.nil?
    if self.class.active_json_model_array_serialization_tuple.nil_data_to_empty_array
      self.values = []
    else
      self.values = nil
    end

    @_active_json_model_values_set = false

    return
  end

  if !json_array.respond_to?(:map) || json_array.is_a?(Hash)
    raise ArgumentError.new("Invalid value specified for json_array. Expected array-like object received #{json_array.class}")
  end

  # Record that we have some sort of values set
  @_active_json_model_values_set = true

  # Iterate over all the allowed attributes
  self.values = json_array.map do |json_val|
    if self.class.active_json_model_array_serialization_tuple.deserialize_proc
      self.class.active_json_model_array_serialization_tuple.deserialize_proc.call(json_val)
    else
      send(self.class.active_json_model_array_serialization_tuple.deserialize_method, json_val)
    end
  end

  # Now that the load is complete, mark dirty tracking as clean
  clear_changes_information
  @_active_json_model_original_values = self.values

  # Invoke any on-load callbacks
  self.class.ancestry_active_json_model_load_callbacks.each do |cb|
    cb.invoke(self)
  end
end

#loaded?Boolean

Was this instance loaded from JSON?

Returns:

  • (Boolean)

    true if loaded from JSON, false otherwise



103
104
105
# File 'lib/active_json_model/array.rb', line 103

def loaded?
  @_active_json_model_loaded
end

#new?Boolean

Is this a new instance that was created without loading, and has yet to be dumped?

Returns:

  • (Boolean)

    true if new, false otherwise



115
116
117
# File 'lib/active_json_model/array.rb', line 115

def new?
  !loaded? && !dumped?
end

#values_set?Boolean

Have the values for this array actually be set, or a defaults coming through?

Returns:

  • (Boolean)

    true if the values have actually been set



121
122
123
# File 'lib/active_json_model/array.rb', line 121

def values_set?
  !!@_active_json_model_values_set
end