1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# frozen_string_literal: true
require "active_model/attribute_set"
require "active_model/attribute/user_provided_default"
module ActiveModel
module Attributes #:nodoc:
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods
included do
attribute_method_suffix "="
class_attribute :attribute_types, :_default_attributes, instance_accessor: false
self.attribute_types = Hash.new(Type.default_value)
self._default_attributes = AttributeSet.new({})
end
module ClassMethods
def attribute(name, type = Type::Value.new, **options)
name = name.to_s
if type.is_a?(Symbol)
type = ActiveModel::Type.lookup(type, **options.except(:default))
end
self.attribute_types = attribute_types.merge(name => type)
define_default_attribute(name, options.fetch(:default, NO_DEFAULT_PROVIDED), type)
define_attribute_method(name)
end
# Returns an array of attribute names as strings
#
# class Person
# include ActiveModel::Attributes
#
# attribute :name, :string
# attribute :age, :integer
# end
#
# Person.attribute_names
# # => ["name", "age"]
def attribute_names
attribute_types.keys
end
private
def define_method_attribute=(name)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
generated_attribute_methods, name, writer: true,
) do |temp_method_name, attr_name_expr|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{temp_method_name}(value)
name = #{attr_name_expr}
write_attribute(name, value)
end
RUBY
end
end
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
private_constant :NO_DEFAULT_PROVIDED
def define_default_attribute(name, value, type)
self._default_attributes = _default_attributes.deep_dup
if value == NO_DEFAULT_PROVIDED
default_attribute = _default_attributes[name].with_type(type)
else
default_attribute = Attribute::UserProvidedDefault.new(
name,
value,
type,
_default_attributes.fetch(name.to_s) { nil },
)
end
_default_attributes[name] = default_attribute
end
end
def initialize(*)
@attributes = self.class._default_attributes.deep_dup
super
end
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
#
# class Person
# include ActiveModel::Model
# include ActiveModel::Attributes
#
# attribute :name, :string
# attribute :age, :integer
# end
#
# person = Person.new(name: 'Francesco', age: 22)
# person.attributes
# # => {"name"=>"Francesco", "age"=>22}
def attributes
@attributes.to_hash
end
# Returns an array of attribute names as strings
#
# class Person
# include ActiveModel::Attributes
#
# attribute :name, :string
# attribute :age, :integer
# end
#
# person = Person.new
# person.attribute_names
# # => ["name", "age"]
def attribute_names
@attributes.keys
end
private
def write_attribute(attr_name, value)
name = attr_name.to_s
name = self.class.attribute_aliases[name] || name
@attributes.write_from_user(name, value)
value
end
def attribute(attr_name)
name = attr_name.to_s
name = self.class.attribute_aliases[name] || name
@attributes.fetch_value(name)
end
# Dispatch target for <tt>*=</tt> attribute methods.
def attribute=(attribute_name, value)
write_attribute(attribute_name, value)
end
end
end
|