Class: Fera::Base

Inherits:
ActiveResource::Base
  • Object
show all
Defined in:
lib/fera/models/base.rb

Direct Known Subclasses

Customer, Media, Order, Product, Rating, Review, Store, Submission, Webhook

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = nil, persisted = nil, options = {}) ⇒ Base

Returns a new instance of Base.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/fera/models/base.rb', line 97

def initialize(attributes = nil, persisted = nil, options = {})
  @options = options.to_h

  dynamic_attributes = attributes.to_h.dup

  association_keys = self.class.has_manys.keys + self.class.has_ones.keys + self.class.belongs_tos.keys

  dynamic_attributes.except!(*(association_keys + association_keys.map(&:to_s)))

  super(dynamic_attributes, persisted)

  return unless attributes.present?

  association_keys.each do |name, _opts|
    if attributes.key?(name.to_s) || attributes.key?(name.to_sym)
      val = attributes.to_h[name.to_s] || attributes.to_h[name.to_sym]
      self.send("#{ name }=", val) if respond_to?("#{ name }=")
    end
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object

Method missing adapters to define public_*_id



291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/fera/models/base.rb', line 291

def method_missing(method_name, *args, &block)
  matcher = method_name.to_s.match(/^(?!is_)([a-z_]+)\?$/) || method_name.to_s.match(/^is_([a-z_]+)\?$/)
  if matcher.present?
    attribute_name = matcher[1]
    return super if attribute_name.blank?

    attribute_name = "is_#{ attribute_name }" unless attribute_name =~ /^is_/
    return super unless known_attribute?(attribute_name.to_s)

    return !!send(attribute_name.to_sym).presence
  end

  super
end

Class Attribute Details

.default_params=(value) ⇒ Object (writeonly)

Sets the attribute default_params

Parameters:

  • value

    the value to set the attribute default_params to.



39
40
41
# File 'lib/fera/models/base.rb', line 39

def default_params=(value)
  @default_params = value
end

Instance Attribute Details

#last_responseObject (readonly)

Returns the value of attribute last_response.



5
6
7
# File 'lib/fera/models/base.rb', line 5

def last_response
  @last_response
end

#last_response_bodyObject (readonly)

Returns the value of attribute last_response_body.



5
6
7
# File 'lib/fera/models/base.rb', line 5

def last_response_body
  @last_response_body
end

#last_response_exceptionObject (readonly)

Returns the value of attribute last_response_exception.



5
6
7
# File 'lib/fera/models/base.rb', line 5

def last_response_exception
  @last_response_exception
end

#last_response_messageObject (readonly)

Returns the value of attribute last_response_message.



5
6
7
# File 'lib/fera/models/base.rb', line 5

def last_response_message
  @last_response_message
end

#optionsObject (readonly)

Returns the value of attribute options.



5
6
7
# File 'lib/fera/models/base.rb', line 5

def options
  @options
end

Class Method Details

.belongs_to(name, options = {}) ⇒ Object



11
12
13
# File 'lib/fera/models/base.rb', line 11

def belongs_to(name, options = {})
  @belongs_tos = @belongs_tos.to_h.merge(name => options)
end

.belongs_tosObject



15
# File 'lib/fera/models/base.rb', line 15

def belongs_tos; @belongs_tos.to_h; end

.create(attributes = {}, extra_params = {}) ⇒ Object



43
44
45
# File 'lib/fera/models/base.rb', line 43

def create(attributes = {}, extra_params = {})
  self.new(attributes, false).tap { |resource| resource.create(extra_params) }
end

.create!(attributes = {}, extra_params = {}) ⇒ Object



49
50
51
# File 'lib/fera/models/base.rb', line 49

def create!(attributes = {}, extra_params = {})
  self.new(attributes, false).tap { |resource| resource.create!(extra_params) }
end

.find_every(options) ⇒ Object



55
56
57
# File 'lib/fera/models/base.rb', line 55

def find_every(options)
  super(add_default_params(options))
end

.find_one(options) ⇒ Object



61
62
63
# File 'lib/fera/models/base.rb', line 61

def find_one(options)
  super(add_default_params(options))
end

.find_single(scope, options) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/fera/models/base.rb', line 67

def find_single(scope, options)
  options = add_default_params(options)
  prefix_options, query_options = split_options(options[:params])
  path = element_path(scope, prefix_options, query_options)

  response = connection.get(path, headers)
  record = instantiate_record(format.decode(response.body), prefix_options)

  record.set_last_response(response)
  record
end

.has_many(name, options = {}) ⇒ Object



17
18
19
# File 'lib/fera/models/base.rb', line 17

def has_many(name, options = {})
  @has_manys = @has_manys.to_h.merge(name => options)
end

.has_manysObject



21
# File 'lib/fera/models/base.rb', line 21

def has_manys; @has_manys.to_h; end

.has_one(name, options = {}) ⇒ Object



23
24
25
# File 'lib/fera/models/base.rb', line 23

def has_one(name, options = {})
  @has_ones = @has_ones.to_h.merge(name => options)
end

.has_onesObject



27
# File 'lib/fera/models/base.rb', line 27

def has_ones; @has_ones.to_h; end

.headersObject



29
30
31
32
33
34
35
36
37
# File 'lib/fera/models/base.rb', line 29

def headers
  if _headers_defined?
    _headers
  elsif superclass != Object && superclass.headers
    superclass.headers
  else
    _headers ||= {} # rubocop:disable Lint/UnderscorePrefixedVariableName
  end
end

.new_element_path(prefix_options = {}, extra_params = {}) ⇒ Object



79
80
81
82
83
# File 'lib/fera/models/base.rb', line 79

def new_element_path(prefix_options = {}, extra_params = {})
  url = "#{ prefix(prefix_options) }#{ collection_name }/new#{ format_extension }"
  url += "?#{ extra_params.to_param }" if extra_params.present?
  url
end

Instance Method Details

#clone_selected_fields(model, fields) ⇒ Object



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/fera/models/base.rb', line 269

def clone_selected_fields(model, fields)
  fields = fields.is_a?(Array) ? fields : fields.to_s.split(',').map(&:strip)

  # find fields
  changed_attributes = HashWithIndifferentAccess.new
  changed_attributes[model.class.primary_key] = model.attributes[model.class.primary_key]
  fields.each do |key|
    if key.include?(':')
      clone_sub_fields(model, key, changed_attributes)
    elsif fields.include?(key)
      changed_attributes[key] = model.attributes[key]
    end
  end

  # create new object
  self.class.new(changed_attributes, true)
end

#clone_with_nilObject



257
258
259
260
261
262
263
264
265
266
267
# File 'lib/fera/models/base.rb', line 257

def clone_with_nil
  # Clone all attributes except the pk and any nested ARes
  cloned = attributes.reject { |k, v| k == self.class.primary_key || v.is_a?(ActiveResource::Base) }.map { |k, v| [k, v.clone] }.to_h
  # Form the new resource - bypass initialize of resource with 'new' as that will call 'load' which
  # attempts to convert hashes into member objects and arrays into collections of objects. We want
  # the raw objects to be cloned so we bypass load by directly setting the attributes hash.
  resource = self.class.new({}, true, { cloned: true })
  resource.prefix_options = prefix_options
  resource.send :instance_variable_set, '@attributes', cloned
  resource
end

#create(extra_params = {}, raise: false) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
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
227
# File 'lib/fera/models/base.rb', line 182

def create(extra_params = {}, raise: false)
  run_callbacks :create do
    data = as_json
    self.class.belongs_tos.merge(self.class.has_ones).each do |name, _opts|
      next unless instance_variable_defined?(:"@#{ name }")

      nested_resource = self.send(name)
      if nested_resource.present? && !nested_resource.persisted?
        nested_resource.validate!
        data[name] = nested_resource.as_json
      end
    end

    self.class.has_manys.each do |name, _opts|
      next unless instance_variable_defined?(:"@#{ name }")

      nested_resource = self.send(name)

      next if nested_resource.nil?

      nested_resource.each do |nested_resource_instance|
        next if nested_resource_instance.persisted?

        nested_resource_instance.validate!

        data[name] ||= []
        data[name] << nested_resource_instance.as_json
      end
    end

    connection.post(collection_path(nil, extra_params), { data: data }.to_json, self.class.headers).tap do |response|
      self.id = id_from_response(response)
      load_attributes_from_response(response)
    end
  end

  true
rescue ActiveResource::ConnectionError => e
  set_last_response(e)

  if raise
    raise(ActiveResource::ResourceInvalid.new(last_response, last_response_message.presence))
  end

  false
end

#create!(extra_params = {}) ⇒ Object



229
230
231
# File 'lib/fera/models/base.rb', line 229

def create!(extra_params = {})
  create(extra_params, raise: true)
end

#created_at=(new_created_at) ⇒ Object



136
137
138
139
140
141
142
# File 'lib/fera/models/base.rb', line 136

def created_at=(new_created_at)
  if new_created_at.is_a?(String)
    super(Time.parse(new_created_at))
  else
    super
  end
end

#destroy!Object



132
133
134
# File 'lib/fera/models/base.rb', line 132

def destroy!
  destroy
end

#known_attribute?(attribute_name) ⇒ Boolean

Returns:

  • (Boolean)


319
320
321
# File 'lib/fera/models/base.rb', line 319

def known_attribute?(attribute_name)
  known_attributes.map(&:to_s).include?(attribute_name.to_s)
end

#load(attributes, *args) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/fera/models/base.rb', line 118

def load(attributes, *args)
  load_result = super(attributes, *args)

  attributes.each do |attr, val|
    if respond_to?("#{ attr }=".to_sym)
      self.send("#{ attr }=".to_sym, val)
    end
  end

  @clean_copy = clone_with_nil if persisted? && !options[:cloned]

  load_result
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/fera/models/base.rb', line 306

def respond_to_missing?(method_name, include_private = false)
  matcher = method_name.to_s.match(/^(?!is_)([a-z_]+)\?$/) || method_name.to_s.match(/^is_([a-z_]+)\?$/)
  if matcher.present?
    attribute_name = matcher[1]
    return super if attribute_name.blank?

    attribute_name = "is_#{ attribute_name }" unless attribute_name =~ /^is_/
    return true if known_attribute?(attribute_name)
  end

  super
end

#save(extra_params = {}, raise: false) ⇒ Object



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/fera/models/base.rb', line 233

def save(extra_params = {}, raise: false)
  run_callbacks :save do
    if new?
      create(extra_params, raise: raise) # We'll raise the error below
    else
      # find changes
      changed_attributes = attributes.filter { |key, value| !@clean_copy.attributes.key?(key) || (@clean_copy.attributes[key] != value) || (key == self.class.primary_key) }
      changed_attributes.reject! { |k| k == 'id' }
      return false unless changed_attributes.keys.any?

      # save
      update(changed_attributes, extra_params, raise: raise)
    end

    @clean_copy = clone_with_nil # Clear changes

    self
  end
end

#save!(extra_params = {}) ⇒ Object



253
254
255
# File 'lib/fera/models/base.rb', line 253

def save!(extra_params = {})
  save(extra_params, raise: true)
end

#set_last_response(result) ⇒ Object

rubocop:disable Naming/AccessorMethodName



323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/fera/models/base.rb', line 323

def set_last_response(result) # rubocop:disable Naming/AccessorMethodName
  response = if result.is_a?(StandardError)
               @last_response_exception = result
               @last_response_exception.response
             else
               @last_response_exception = nil
               result
             end

  @last_response = response
  @last_response_body = response.body.present? ? self.class.format.decode(response.body) : nil
  @last_response_message = last_response_body.to_h['message']
end

#update(changed_attributes, extra_params = {}, raise: false) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/fera/models/base.rb', line 152

def update(changed_attributes, extra_params = {}, raise: false)
  run_callbacks(:update) do
    connection.put(element_path(prefix_options, extra_params), changed_attributes.to_json, self.class.headers).tap do |response|
      load_attributes_from_response(response)
    end

    load(changed_attributes)
  end

  true
rescue ActiveResource::ConnectionError => e
  set_last_response(e)

  if raise
    raise(ActiveResource::ResourceInvalid.new(last_response, last_response_message.presence))
  end

  false
end

#update!(changed_attributes, extra_params = {}) ⇒ Object



172
173
174
# File 'lib/fera/models/base.rb', line 172

def update!(changed_attributes, extra_params = {})
  update(changed_attributes, extra_params, raise: true)
end

#updated_at=(new_updated_at) ⇒ Object



144
145
146
147
148
149
150
# File 'lib/fera/models/base.rb', line 144

def updated_at=(new_updated_at)
  if new_updated_at.is_a?(String)
    super(Time.parse(new_updated_at))
  else
    super
  end
end

#valid?(_context = nil) ⇒ Boolean

Returns:

  • (Boolean)


176
177
178
# File 'lib/fera/models/base.rb', line 176

def valid?(_context = nil)
  super()
end