Class: NestedRecord::Base

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Callbacks
Includes:
ActiveModel::Attributes, ActiveModel::Dirty, ActiveModel::Model, ActiveModel::Validations::Callbacks, Macro
Defined in:
lib/nested_record/base.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = nil) ⇒ Base

Returns a new instance of Base.



225
226
227
228
229
# File 'lib/nested_record/base.rb', line 225

def initialize(attributes = nil)
  super
  self.type = self.class.instance_type if self.class.deep_inherited? && !(attributes&.key?('type') || attributes&.key?(:type))
  _run_initialize_callbacks
end

Class Method Details

.attribute(name, *args, primary: false, **options) ⇒ Object



106
107
108
109
110
# File 'lib/nested_record/base.rb', line 106

def attribute(name, *args, primary: false, **options)
  super(name, *args, **options).tap do
    primary_key(name) if primary
  end
end

.attributes_builderObject

:nodoc:



13
14
15
16
17
18
# File 'lib/nested_record/base.rb', line 13

def attributes_builder # :nodoc:
  unless defined?(@attributes_builder) && @attributes_builder
    @attributes_builder = ActiveModel::AttributeSet::Builder.new(attribute_types, _default_attributes)
  end
  @attributes_builder
end

.collection_classObject



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/nested_record/base.rb', line 61

def collection_class
  return const_get(collection_class_name, false) if const_defined?(collection_class_name, false)
  record_class = self
  collection_superclass = deep_inherited? ? superclass.collection_class : NestedRecord::Collection
  const_set(
    collection_class_name,
    Class.new(collection_superclass) do
      @record_class = record_class
    end
  )
end

.collection_methods(&block) ⇒ Object

Raises:

  • (ArgumentError)


73
74
75
76
# File 'lib/nested_record/base.rb', line 73

def collection_methods(&block)
  raise ArgumentError, 'block is required for .collection_methods' unless block
  collection_class.class_eval(&block)
end

.deep_inherited?Boolean

Returns:

  • (Boolean)


31
32
33
# File 'lib/nested_record/base.rb', line 31

def deep_inherited?
  @deep_inherted == true
end

.def_primary_uuid(name) ⇒ Object



119
120
121
# File 'lib/nested_record/base.rb', line 119

def def_primary_uuid(name)
  attribute name, :string, default: -> { SecureRandom.uuid }, primary: true
end

.find_subtype(type_name) ⇒ Object



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
# File 'lib/nested_record/base.rb', line 137

def find_subtype(type_name)
  return self unless type_name.present?

  type_name = type_name.to_s.camelize

  subclass = local_subtype(type_name)
  subclass ||=
    begin
      if type_name.start_with?('::')
        ActiveSupport::Dependencies.constantize(type_name)
      elsif subtypes_store_full?
        ActiveSupport::Dependencies.constantize(type_name)
      elsif subtypes_namespace
        ActiveSupport::Dependencies.safe_constantize("#{subtypes_namespace}::#{type_name}") || ActiveSupport::Dependencies.constantize(type_name)
      else
        NestedRecord.lookup_const(self, type_name)
      end
    rescue NameError
      raise NestedRecord::InvalidTypeError, "Failed to locate type '#{type_name}'"
    end
  unless subclass.is_a? Class
    raise NestedRecord::InvalidTypeError, "Invalid type '#{type_name}': should be a class"
  end
  unless subclass <= self
    raise NestedRecord::InvalidTypeError, "Invalid type '#{type_name}': should be a subclass of #{self}"
  end
  subclass
end

.has_attribute?(attr_name) ⇒ Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/nested_record/base.rb', line 133

def has_attribute?(attr_name)
  attribute_types.key?(attr_name.to_s)
end

.inherited(klass) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'lib/nested_record/base.rb', line 20

def inherited(klass)
  parent = self
  if parent < NestedRecord::Base
    klass.class_eval do
      attribute :type, :string unless parent.has_attribute? :type
      @deep_inherted = true
    end
  end
  super
end

.instance_typeObject



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

def instance_type
  @instance_type ||=
    if subtypes_underscored?
      type_const.underscore
    else
      type_const.dup
    end
end

.instantiate(attributes) ⇒ Object



53
54
55
56
57
58
59
# File 'lib/nested_record/base.rb', line 53

def instantiate(attributes)
  klass = find_subtype(attributes['type'])
  attributes = klass.attributes_builder.build_from_database(attributes)
  klass.allocate.tap do |instance|
    instance.init_with_attributes(attributes)
  end
end

.new(attributes = nil) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/nested_record/base.rb', line 35

def new(attributes = nil)
  if local_subtype?
    return super
  else
    if attributes
      attributes = attributes.stringify_keys
      klass = find_subtype(attributes['type'])
    else
      klass = self
    end
  end
  if self == klass
    super
  else
    klass.new(attributes)
  end
end

.primary_key(*attributes) ⇒ Object



112
113
114
115
116
117
# File 'lib/nested_record/base.rb', line 112

def primary_key(*attributes)
  unless attributes.empty?
    self.primary_key = attributes
  end
  @primary_key
end

.primary_key=(attributes) ⇒ Object

Raises:

  • (ArgumentError)


123
124
125
126
127
# File 'lib/nested_record/base.rb', line 123

def primary_key=(attributes)
  attributes = Array(attributes)
  raise ArgumentError, 'primary_key cannot be an empty array' if attributes.empty?
  @primary_key = attributes.map(&:to_s)
end

.subtype(name, &block) ⇒ Object

Raises:

  • (NotImplementedError)


95
96
97
98
99
100
101
102
103
104
# File 'lib/nested_record/base.rb', line 95

def subtype(name, &block)
  raise NotImplementedError, 'TODO: Subtyping from local subtype is not supported at the moment' if local_subtype?
  class_name = name.to_s.camelize
  subtype = Class.new(self) do
    @local_subtype = true
    @type_const = class_name
    class_eval(&block) if block
  end
  local_subtypes!.const_set(class_name, subtype)
end

.subtypes(options) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/nested_record/base.rb', line 78

def subtypes(options)
  raise NestedRecord::ConfigurationError, '.subtypes is supported only for base classes' if deep_inherited?
  if options[:full] && options[:namespace]
    raise NestedRecord::ConfigurationError, ':full and :namespace options cannot be used together'
  end
  @subtypes_options = options
end

.type_for_attribute(attr_name) ⇒ Object



129
130
131
# File 'lib/nested_record/base.rb', line 129

def type_for_attribute(attr_name)
  attribute_types[attr_name.to_s]
end

Instance Method Details

#==(other) ⇒ Object



236
237
238
# File 'lib/nested_record/base.rb', line 236

def ==(other)
  attributes == other.attributes
end

#as_jsonObject



240
241
242
# File 'lib/nested_record/base.rb', line 240

def as_json
  attributes.transform_values(&:as_json)
end

#init_with_attributes(attributes) ⇒ Object



231
232
233
234
# File 'lib/nested_record/base.rb', line 231

def init_with_attributes(attributes)
  @attributes = attributes
  _run_initialize_callbacks
end

#inspectObject



244
245
246
247
# File 'lib/nested_record/base.rb', line 244

def inspect
  as = attributes.except('type').map { |k,v| "#{k}: #{v.inspect}" }
  "#<#{self.class.name} #{as.join(', ')}>"
end

#match?(attrs) ⇒ Boolean

Returns:

  • (Boolean)


263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/nested_record/base.rb', line 263

def match?(attrs)
  attrs.all? do |attr, others|
    case attr
    when :_is_a?, '_is_a?'
      is_a? others
    when :_instance_of?, '_instance_of?'
      instance_of? others
    when :_not_equal?, '_not_equal?'
      !equal?(others)
    else
      ours = read_attribute(attr)
      if others.is_a? Array
        others.include? ours
      else
        others == ours
      end
    end
  end
end

#query_attribute(attr) ⇒ Object



253
254
255
256
257
258
259
260
261
# File 'lib/nested_record/base.rb', line 253

def query_attribute(attr)
  value = read_attribute(attr)

  case value
  when true        then true
  when false, nil  then false
  else !value.blank?
  end
end

#read_attribute(attr) ⇒ Object



249
250
251
# File 'lib/nested_record/base.rb', line 249

def read_attribute(attr)
  attribute(attr)
end