Completed
Pull Request — master (#5)
by Marcelo
34s
created

ClassMethods.coerce_adapter()   A

Complexity

Conditions 4

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
dl 0
loc 7
rs 9.2
c 0
b 0
f 0
1
require 'forwardable'
2
require 'koine/attributes/version'
3
require 'koine/attributes/adapter/base'
4
5
# provides the following API
6
#
7
# @example using attributes
8
#   class Person
9
#     include Koine::Attributes
10
#
11
#     attributes do
12
#       attribute :name, :string
13
#       attribute :birthday, :date
14
#
15
#       # or
16
#       attribute :birthday, Koine::Attributes::Adapter::Date.new
17
#     end
18
#   end
19
#
20
#   peson = Person.new
21
#   person.name = 'John Doe'
22
#   person.birtday = '2001-02-31' # Date Object can also be given
23
#
24
#   person.name # => 'John Doe'
25
#   person.birtday # => #<Date 2001-02-31>
26
#
27
# @example custom attribute options
28
#
29
#     attributes do
30
#       attribute :name, Koine::Attributes::Adapters::Date.new.with_default('guest')
31
#
32
#       # or
33
#       attribute :name, :string, ->(adapter) { adapter.with_default('guest') }
34
#     end
35
#
36
# @example Constructor for attributes
37
#
38
#   class Person
39
#     include Koine::Attributes
40
#
41
#     attributes initializer: true do
42
#       attribute :name, :string
43
#       attribute :birthday, :date
44
#     end
45
#   end
46
#
47
#   person = Person.new(name: 'John Doe', birthday: '2001-01-31')
48
#
49
#   # foo: attribute will raise error with strict mode
50
#   person = Person.new(name: 'John Doe', birthday: '2001-01-31', foo: :bar)
51
#
52
# @example Constructor for attributes withouth strict mode
53
#
54
#   class Person
55
#     include Koine::Attributes
56
#
57
#     attributes initializer: { strict: false } do
58
#       attribute :name, :string
59
#       attribute :birthday, :date
60
#     end
61
#   end
62
#
63
#   # foo will be ignored
64
#   person = Person.new(name: 'John Doe', birthday: '2001-01-31', foo: :bar)
65
#
66
# @example Override constructor
67
#
68
#   class Person
69
#     include Koine::Attributes
70
#
71
#     attr_reader :foo
72
#
73
#     attributes initializer: true do
74
#       attribute :name, :string
75
#       attribute :birthday, :date
76
#     end
77
#
78
#     def initializ(attributes = {})
79
#       @foo = attributes.delete(:foo)
80
#
81
#       initialize_attributes(attributes) # protected
82
#     end
83
#   end
84
#
85
#   person = Person.new(name: 'John Doe', birthday: '2001-01-31', foo: :bar)
86
#   person.foo # => :bar
87
#
88
# @example
89
#  class Location
90
#    include Koine::Attributes
91
#
92
#    attributes initializer: { freeze: true } do
93
#      attribute :lat, :float
94
#      attribute :lon, :float
95
#    end
96
#  end
97
#
98
#  location = Location.new(lat: 1, lon: 2)
99
#  new_location = location.with_lon(3)
100
#
101
module Koine
102
  module Attributes
103
    autoload :Attributes, 'koine/attributes/attributes'
104
    autoload :AttributesFactory, 'koine/attributes/attributes_factory'
105
106
    module Adapter
107
      autoload :Boolean, 'koine/attributes/adapter/boolean'
108
      autoload :Date, 'koine/attributes/adapter/date'
109
      autoload :Time, 'koine/attributes/adapter/time'
110
      autoload :Float, 'koine/attributes/adapter/float'
111
      autoload :Integer, 'koine/attributes/adapter/integer'
112
      autoload :String, 'koine/attributes/adapter/string'
113
    end
114
115
    Error = Class.new(StandardError)
116
117
    def self.included(base)
118
      base.extend(Forwardable)
119
      base.extend(ClassMethods)
120
    end
121
122
    module ClassMethods
123
      def attributes(options = {}, &block)
124
        @builder = true
125
        @_attributes_factory ||= AttributesFactory.new(options)
126
127
        instance_eval do
128
          define_method :attributes do
129
            @_attributes ||= self.class.instance_variable_get(:@_attributes_factory).create(self)
130
          end
131
132
          if options[:initializer]
133
            define_method(:initialize) { |*args| attributes.set_values(*args) }
134
          end
135
        end
136
137
        class_eval(&block)
138
        @builder = nil
139
      end
140
141
      def attribute(name, adapter, lambda_arg = nil, &block)
142
        unless @builder
143
          raise Error, 'You must call .attribute inside the .attributes block'
144
        end
145
146
        block = lambda_arg || block
147
        instance_variable_get(:@_attributes_factory).add_attribute(name, adapter, &block)
148
149
        instance_eval do
150
          def_delegators :attributes, name, "#{name}=", "with_#{name}", :==
151
        end
152
      end
153
    end
154
  end
155
end
156