Class: Reynard::Model

Inherits:
BasicObject
Extended by:
Forwardable
Defined in:
lib/reynard/model.rb

Overview

Superclass for dynamic classes generated by the object builder.

Direct Known Subclasses

Server

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes) ⇒ Model

Returns a new instance of Model.



16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/reynard/model.rb', line 16

def initialize(attributes)
  if attributes.respond_to?(:each) && attributes.respond_to?(:keys)
    @attributes = {}
    @snake_cases = self.class.snake_cases(attributes.keys)
    self.attributes = attributes
  else
    ::Kernel.raise(
      ::ArgumentError,
      self.class.attributes_error_message(attributes)
    )
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(attribute_name) ⇒ Object

Until we can set accessors based on the schema



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/reynard/model.rb', line 46

def method_missing(attribute_name, *)
  return false unless @attributes

  attribute_name = attribute_name.to_s
  if @attributes.key?(attribute_name)
    @attributes[attribute_name]
  else
    @attributes.fetch(@snake_cases.fetch(attribute_name))
  end
rescue ::KeyError
  ::Kernel.raise ::NoMethodError, "undefined method `#{attribute_name}' for #{inspect}"
end

Class Attribute Details

.inflectorObject



82
83
84
# File 'lib/reynard/model.rb', line 82

def self.inflector
  @inflector ||= ::Reynard::Inflector.new
end

.schemaObject

Holds references to the full schema for the model if available.



11
12
13
# File 'lib/reynard/model.rb', line 11

def schema
  @schema
end

Class Method Details

.attributes_error_message(attributes) ⇒ Object



95
96
97
98
99
# File 'lib/reynard/model.rb', line 95

def self.attributes_error_message(attributes)
  'Models must be intialized with an enumerable object that behaves like a Hash, got: ' \
    "`#{attributes.inspect}'. Usually this means the schema defined in the OpenAPI " \
    "specification doesn't fit the payload in the HTTP response."
end

.cast(name, value) ⇒ Object



72
73
74
75
76
77
78
79
80
# File 'lib/reynard/model.rb', line 72

def self.cast(name, value)
  return if value.nil?
  return value unless schema

  property = schema.property_schema(name)
  return value unless property

  ::Reynard::ObjectBuilder.new(schema: property, inflector:, parsed_body: value).call
end

.snake_cases(property_names) ⇒ Object



86
87
88
89
90
91
92
93
# File 'lib/reynard/model.rb', line 86

def self.snake_cases(property_names)
  property_names.each_with_object({}) do |property_name, snake_cases|
    snake_case = inflector.snake_case(property_name)
    next if snake_case == property_name

    snake_cases[snake_case] = property_name
  end
end

Instance Method Details

#attributes=(attributes) ⇒ Object



39
40
41
42
43
# File 'lib/reynard/model.rb', line 39

def attributes=(attributes)
  attributes.each do |name, value|
    @attributes[name.to_s] = self.class.cast(name, value)
  end
end

#inspectObject



35
36
37
# File 'lib/reynard/model.rb', line 35

def inspect
  "#<#{self.class.name}:0x#{object_id.to_s(16)}>"
end

#respond_to_missing?(attribute_name) ⇒ Boolean

Returns:

  • (Boolean)


59
60
61
62
63
64
65
66
# File 'lib/reynard/model.rb', line 59

def respond_to_missing?(attribute_name, *)
  attribute_name = attribute_name.to_s
  return true if @attributes.key?(attribute_name)

  @snake_cases.key?(attribute_name) && @attributes.key?(@snake_cases[attribute_name])
rescue ::NameError
  false
end

#try(attribute_name) ⇒ Object



68
69
70
# File 'lib/reynard/model.rb', line 68

def try(attribute_name)
  respond_to_missing?(attribute_name) ? send(attribute_name) : nil
end