/
attributes.rb
153 lines (141 loc) · 4.2 KB
/
attributes.rb
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# frozen_string_literal: true
module ActiveModel
# = Active \Model \Attributes
#
# The Attributes module allows models to define attributes beyond simple Ruby
# readers and writers. Similar to Active Record attributes, which are
# typically inferred from the database schema, Active Model Attributes are
# aware of data types, can have default values, and can handle casting and
# serialization.
#
# To use Attributes, include the module in your model class and define your
# attributes using the +attribute+ macro. It accepts a name, a type, a default
# value, and any other options supported by the attribute type.
#
# ==== Examples
#
# class Person
# include ActiveModel::Attributes
#
# attribute :name, :string
# attribute :active, :boolean, default: true
# end
#
# person = Person.new
# person.name = "Volmer"
#
# person.name # => "Volmer"
# person.active # => true
module Attributes
extend ActiveSupport::Concern
include ActiveModel::AttributeRegistration
include ActiveModel::AttributeMethods
include ActiveModel::BeforeTypeCast
included do
attribute_method_suffix "=", parameters: "value"
end
module ClassMethods
##
# :call-seq: attribute(name, cast_type = nil, default: nil, **options)
#
# Defines a model attribute. In addition to the attribute name, a cast
# type and default value may be specified, as well as any options
# supported by the given cast type.
#
# class Person
# include ActiveModel::Attributes
#
# attribute :name, :string
# attribute :active, :boolean, default: true
# end
#
# person = Person.new
# person.name = "Volmer"
#
# person.name # => "Volmer"
# person.active # => true
def attribute(name, ...)
super
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, owner:)
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
owner, name, writer: true,
) do |temp_method_name, attr_name_expr|
owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_model) do |batch|
batch <<
"def #{temp_method_name}(value)" <<
" _write_attribute(#{attr_name_expr}, value)" <<
"end"
end
end
end
end
def initialize(*) # :nodoc:
@attributes = self.class._default_attributes.deep_dup
super
end
def initialize_dup(other) # :nodoc:
@attributes = @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::Attributes
#
# attribute :name, :string
# attribute :age, :integer
# end
#
# person = Person.new
# person.name = "Francesco"
# person.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
def freeze # :nodoc:
@attributes = @attributes.clone.freeze unless frozen?
super
end
private
def _write_attribute(attr_name, value)
@attributes.write_from_user(attr_name, value)
end
alias :attribute= :_write_attribute
def attribute(attr_name)
@attributes.fetch_value(attr_name)
end
end
end