Class: ActiveValue::Base
- Inherits:
-
Object
- Object
- ActiveValue::Base
- Defined in:
- lib/active_value/base.rb
Overview
ActiveValue::Base is base class for immutable value object that has interfaces like ActiveRecord. In a class inherited this class, constant variables get the behavior like records of ActiveRecord.
Usage.
1. Define the target class inherited from ActiveValue::Base
2. List attributes of the object using attr_accessor
3. Declare constant variables as this class
Example.
class QuestionType < ActiveValue::Base
attr_accessor :id, :symbol, :name
CHECKBOX = new id: 1, symbol: :checkbox, name: "Check Box"
RADIO = new id: 2, symbol: :radio, name: "Radio Button"
SELECTBOX = new id: 3, symbol: :select, name: "Select Box"
TEXT = new id: 4, symbol: :text, name: "Text Box"
TEXTAREA = new id: 5, symbol: :textarea, name: "Text Area"
end
QuestionType.find(1)
=> QuestionType::CHECKBOX
QuestionType.find(1).name
=> "Check Box"
QuestionType.find(1).checkbox?
=> true
QuestionType.pluck(:id, :name)
=> [[1, "Check Box"], [2, "Radio Button"], [3, "Select Box"], ...]
Class Method Summary collapse
-
.accessors ⇒ Object
Get accessors the overrided method saved.
-
.all ⇒ Object
Get all constant instances.
-
.attr_accessor(*several_variants) ⇒ Object
Wrap default attr_accessor method in order to save accessor defined order.
-
.define_question_methods(attr_name = :symbol) ⇒ Object
Automatically these methods are defined in this version.
-
.find(index) ⇒ Object
Getter interface by the id element like ‘find` method in ActiveRecord.
-
.find_by(conditions) ⇒ Object
Getter interface by the argument element like ‘find_by` method in ActiveRecord.
-
.method_missing(method, *args, &block) ⇒ Object
Delegate undefined method calls to ‘all` method (returns Array).
-
.patched_dup(object) ⇒ Object
Wrapper dup method for can’t dup on Fixnum#dup (NilClass etc.) before Ruby 2.4.
-
.pluck(*accessors) ⇒ Object
Get attributes.
-
.unsorted_all ⇒ Object
Get all constant instances.
Instance Method Summary collapse
-
#<=>(another) ⇒ Object
Define the spaceship operator.
-
#==(another) ⇒ Object
(also: #eql?)
Define the equal operator.
-
#hash ⇒ Object
Hash method for equivalence comparison with ‘eql?` method.
-
#initialize(attributes = {}) ⇒ Base
constructor
If self instance is passed as an argument, create a new instance that has copied attributes.
- #inspect ⇒ Object
-
#method_missing(method, *args, &block) ⇒ Object
If objects have symbol attributes, the objects can be checked equivalence by the method named symbol + ‘?`.
-
#to_deep_hash ⇒ Object
(also: #to_h)
Convert to hash with deep copy.
- #to_json ⇒ Object
-
#to_shallow_hash ⇒ Object
Convert to hash with shallow copy.
Constructor Details
#initialize(attributes = {}) ⇒ Base
If self instance is passed as an argument, create a new instance that has copied attributes. (it’s like copy constructor by shallow copy) Hash instance is passed, the hash attributes apply a new instance.
96 97 98 99 100 101 |
# File 'lib/active_value/base.rb', line 96 def initialize(attributes = {}) case attributes when self.class then self.class.accessors.map(&:to_s).each { |attribute| public_send(attribute + '=', attributes.public_send(attribute)) } when Hash then attributes.stringify_keys.each { |key, value| public_send(key + '=', value) if respond_to?(key + '=') } end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
If objects have symbol attributes, the objects can be checked equivalence by the method named symbol + ‘?`
104 105 106 107 108 109 110 111 |
# File 'lib/active_value/base.rb', line 104 def method_missing(method, *args, &block) object = self.class.unsorted_all.find { |object| object.respond_to?(:symbol) && method.to_s == object.public_send(:symbol).to_s + '?' } if object.nil? super else self == object end end |
Class Method Details
.accessors ⇒ Object
Get accessors the overrided method saved.
80 81 82 83 84 85 |
# File 'lib/active_value/base.rb', line 80 def self.accessors readers = instance_methods.reject { |attr| attr.to_s[-1] == '=' } writers = instance_methods.select { |attr| attr.to_s[-1] == '=' }.map { |attr| attr.to_s.chop.to_sym } accessors = readers & writers - [:!] Array(@accessors) | accessors.reverse! end |
.all ⇒ Object
Get all constant instances. (Sorted by the first defined accessor)
57 58 59 |
# File 'lib/active_value/base.rb', line 57 def self.all unsorted_all.sort end |
.attr_accessor(*several_variants) ⇒ Object
Wrap default attr_accessor method in order to save accessor defined order.
74 75 76 77 |
# File 'lib/active_value/base.rb', line 74 def self.attr_accessor(*several_variants) @accessors = *several_variants super end |
.define_question_methods(attr_name = :symbol) ⇒ Object
Automatically these methods are defined in this version. This method is remained only for compatibility.
67 68 69 70 71 |
# File 'lib/active_value/base.rb', line 67 def self.define_question_methods(attr_name = :symbol) unsorted_all.each do |object| define_method(object.public_send(attr_name).to_s + '?') { self == object } if object.respond_to?(attr_name) end end |
.find(index) ⇒ Object
Getter interface by the id element like ‘find` method in ActiveRecord.
39 40 41 42 |
# File 'lib/active_value/base.rb', line 39 def self.find(index) object = all.bsearch { |object| object.public_send(:id) >= index } object&.id == index ? object : nil end |
.find_by(conditions) ⇒ Object
Getter interface by the argument element like ‘find_by` method in ActiveRecord.
45 46 47 48 49 |
# File 'lib/active_value/base.rb', line 45 def self.find_by(conditions) unsorted_all.find do |object| conditions.all? { |key, value| object.public_send(key) == value } end end |
.method_missing(method, *args, &block) ⇒ Object
Delegate undefined method calls to ‘all` method (returns Array).
34 35 36 |
# File 'lib/active_value/base.rb', line 34 def self.method_missing(method, *args, &block) all.public_send(method, *args, &block) end |
.patched_dup(object) ⇒ Object
Wrapper dup method for can’t dup on Fixnum#dup (NilClass etc.) before Ruby 2.4
88 89 90 91 92 |
# File 'lib/active_value/base.rb', line 88 def self.patched_dup(object) object.dup rescue TypeError object end |
.pluck(*accessors) ⇒ Object
Get attributes
62 63 64 |
# File 'lib/active_value/base.rb', line 62 def self.pluck(*accessors) map { |record| Array(accessors).map { |accessor| record.public_send(accessor) } }.map { |array| array.size == 1 ? array.first : array } end |
.unsorted_all ⇒ Object
Get all constant instances. (Unsorted)
52 53 54 |
# File 'lib/active_value/base.rb', line 52 def self.unsorted_all constants.collect { |name| const_get(name) }.select { |object| object.instance_of?(self) } end |
Instance Method Details
#<=>(another) ⇒ Object
Define the spaceship operator. Compare self with another by the first defined accessor. (In many cases, it’s ‘id` implicitly)
153 154 155 156 |
# File 'lib/active_value/base.rb', line 153 def <=>(another) attr = self.class.accessors.first || :object_id public_send(attr) <=> another.public_send(attr) if respond_to?(attr) && another.respond_to?(attr) end |
#==(another) ⇒ Object Also known as: eql?
Define the equal operator. ‘A == B` expression means every attribute has same value. (NOT object_id comparison)
142 143 144 |
# File 'lib/active_value/base.rb', line 142 def ==(another) self.class.accessors.all? { |attr| respond_to?(attr) && another.respond_to?(attr) && public_send(attr) == another.public_send(attr) } end |
#hash ⇒ Object
Hash method for equivalence comparison with ‘eql?` method. (Referenced by `Enumerable#uniq` method etc.)
148 149 150 |
# File 'lib/active_value/base.rb', line 148 def hash to_json.hash end |
#inspect ⇒ Object
136 137 138 139 |
# File 'lib/active_value/base.rb', line 136 def inspect hash = to_shallow_hash Hash === hash ? '#<' << self.class.name.split('::').last << ' ' << hash.map { |key, value| key.to_s << ': ' << value.inspect }.join(', ') << '>' : hash.inspect end |
#to_deep_hash ⇒ Object Also known as: to_h
Convert to hash with deep copy. If values include collections(Hash, Array, etc.), search and convert into collections recursively.
119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/active_value/base.rb', line 119 def to_deep_hash scan = ->(value) do case value when Hash then value.each_with_object({}) { |(k, v), h| h[k] = scan.call(v) } when Array then value.map { |v| scan.call(v) } when Base then scan.call(value.to_shallow_hash) else self.class.patched_dup(value) end end self.class.accessors.each_with_object({}) { |key, hash| hash[key] = scan.call(public_send(key)) } end |
#to_json ⇒ Object
132 133 134 |
# File 'lib/active_value/base.rb', line 132 def to_json JSON.generate(to_h) end |
#to_shallow_hash ⇒ Object
Convert to hash with shallow copy. If values include collections(Hash, Array, etc.), search and convert without elements of the collection.
114 115 116 |
# File 'lib/active_value/base.rb', line 114 def to_shallow_hash self.class.accessors.each_with_object({}) { |key, hash| hash[key] = self.class.patched_dup(public_send(key)) } end |