toolchain 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +34 -0
- data/LICENSE +22 -0
- data/README.md +254 -0
- data/Rakefile +1 -0
- data/lib/toolchain.rb +2 -0
- data/lib/toolchain/attributes.rb +154 -0
- data/lib/toolchain/attributes/configuration.rb +41 -0
- data/lib/toolchain/attributes/errors.rb +5 -0
- data/lib/toolchain/attributes/errors/invalid_hash_transformation.rb +4 -0
- data/lib/toolchain/attributes/errors/invalid_mass_assignment.rb +4 -0
- data/lib/toolchain/attributes/errors/type_mismatch.rb +4 -0
- data/lib/toolchain/attributes/ext/boolean.rb +4 -0
- data/lib/toolchain/attributes/helpers.rb +72 -0
- data/lib/toolchain/validations.rb +103 -0
- data/lib/toolchain/validations/delegation.rb +31 -0
- data/lib/toolchain/validations/delegator.rb +51 -0
- data/lib/toolchain/validations/helpers.rb +58 -0
- data/lib/toolchain/validations/validation_errors.rb +82 -0
- data/lib/toolchain/validations/validators.rb +12 -0
- data/lib/toolchain/validations/validators/acceptance.rb +25 -0
- data/lib/toolchain/validations/validators/base.rb +54 -0
- data/lib/toolchain/validations/validators/confirmation.rb +39 -0
- data/lib/toolchain/validations/validators/email.rb +26 -0
- data/lib/toolchain/validations/validators/exclusion.rb +29 -0
- data/lib/toolchain/validations/validators/format.rb +25 -0
- data/lib/toolchain/validations/validators/inclusion.rb +29 -0
- data/lib/toolchain/validations/validators/length.rb +60 -0
- data/lib/toolchain/validations/validators/presence.rb +25 -0
- data/lib/toolchain/version.rb +3 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/toolchain/attributes/attribute_spec.rb +47 -0
- data/spec/toolchain/attributes/attributes_spec.rb +193 -0
- data/spec/toolchain/attributes/base_helper.rb +37 -0
- data/spec/toolchain/attributes/boolean_spec.rb +38 -0
- data/spec/toolchain/attributes/configuration_spec.rb +33 -0
- data/spec/toolchain/attributes/date_time_spec.rb +21 -0
- data/spec/toolchain/attributes/hash_spec.rb +61 -0
- data/spec/toolchain/attributes/include_attributes_spec.rb +19 -0
- data/spec/toolchain/attributes/initializer_spec.rb +32 -0
- data/spec/toolchain/validations/base_helper.rb +2 -0
- data/spec/toolchain/validations/custom_validations_spec.rb +26 -0
- data/spec/toolchain/validations/delegation_spec.rb +70 -0
- data/spec/toolchain/validations/include_validations_spec.rb +20 -0
- data/spec/toolchain/validations/inheritence_spec.rb +26 -0
- data/spec/toolchain/validations/multiple_validations_spec.rb +19 -0
- data/spec/toolchain/validations/nested_validations_spec.rb +68 -0
- data/spec/toolchain/validations/validation_errors_spec.rb +9 -0
- data/spec/toolchain/validations/validators/acceptance_spec.rb +48 -0
- data/spec/toolchain/validations/validators/confirmation_spec.rb +86 -0
- data/spec/toolchain/validations/validators/email_spec.rb +41 -0
- data/spec/toolchain/validations/validators/exclusion_spec.rb +53 -0
- data/spec/toolchain/validations/validators/format_spec.rb +44 -0
- data/spec/toolchain/validations/validators/inclusion_spec.rb +50 -0
- data/spec/toolchain/validations/validators/length_spec.rb +134 -0
- data/spec/toolchain/validations/validators/presence_spec.rb +34 -0
- data/toolchain.gemspec +21 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 988af450afb20a81dae7333ff7ded62a2fc6ca52
|
4
|
+
data.tar.gz: f0cecb5a9bfe8ec82cbadab21019f4267f8ae5e5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3f707cd58b11de78ce0166874d9712d6197e572dcd6919787355a56cf2821b52127b0748732d2207f238721d45a25bf28c1fc8dc7ffc5d56255682accc1fe6d0
|
7
|
+
data.tar.gz: 62c64f506a01f32f050f17b4418e1828a13e8c8f9f0dae62b324b3af615e5cc64f469f69af33fd2c7decb651e6fd48c4fd8eab4f579beaf69e5ab4738e6479f6
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format documentation
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.2.5)
|
5
|
+
docile (1.1.3)
|
6
|
+
metaclass (0.0.4)
|
7
|
+
mocha (1.0.0)
|
8
|
+
metaclass (~> 0.0.1)
|
9
|
+
multi_json (1.9.0)
|
10
|
+
rake (10.1.1)
|
11
|
+
rspec (2.14.1)
|
12
|
+
rspec-core (~> 2.14.0)
|
13
|
+
rspec-expectations (~> 2.14.0)
|
14
|
+
rspec-mocks (~> 2.14.0)
|
15
|
+
rspec-core (2.14.8)
|
16
|
+
rspec-expectations (2.14.5)
|
17
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
18
|
+
rspec-mocks (2.14.6)
|
19
|
+
simplecov (0.8.2)
|
20
|
+
docile (~> 1.1.0)
|
21
|
+
multi_json
|
22
|
+
simplecov-html (~> 0.8.0)
|
23
|
+
simplecov-html (0.8.0)
|
24
|
+
yard (0.8.7.3)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
mocha
|
31
|
+
rake
|
32
|
+
rspec
|
33
|
+
simplecov
|
34
|
+
yard
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) Machinery ( http://machinery.io )
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
# Toolchain
|
2
|
+
|
3
|
+
Collection of Ruby light-weight modules that enhance plain Ruby classes with zero runtime dependencies.
|
4
|
+
|
5
|
+
##### Available modules
|
6
|
+
|
7
|
+
* Attributes
|
8
|
+
* For defining rich attributes without a data store mapper like ActiveRecord
|
9
|
+
* Useful for applying principles such as the Single Responsibility Principe (SRP)
|
10
|
+
* Validations
|
11
|
+
* Inspired by `ActiveModel::Validations`
|
12
|
+
* Supports nested validations for Hash data types
|
13
|
+
* Useful for applying principles such as the Single Responsibility Principe (SRP)
|
14
|
+
* Lightweight (~250 LOC) instead of `ActiveModel::Validations` (~1600 LOC)
|
15
|
+
* Note: This isn't a clone. Don't expect all of the features in `ActiveModel::Validations` to be available here. Part of the public api behaves in a similar fashion, there are however also a bunch of subtle differences.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
In `Gemfile`:
|
20
|
+
|
21
|
+
```rb
|
22
|
+
gem "toolchain"
|
23
|
+
```
|
24
|
+
|
25
|
+
Or, if you wish to only include certain modules:
|
26
|
+
|
27
|
+
In `Gemfile`:
|
28
|
+
|
29
|
+
```rb
|
30
|
+
gem "toolchain", require: false
|
31
|
+
```
|
32
|
+
|
33
|
+
Then from anywhere in your program/app:
|
34
|
+
|
35
|
+
```rb
|
36
|
+
require "toolchain/attributes"
|
37
|
+
require "toolchain/validations"
|
38
|
+
```
|
39
|
+
|
40
|
+
## Modules
|
41
|
+
|
42
|
+
Below follows a list of modules.
|
43
|
+
|
44
|
+
### Toolchain::Attributes
|
45
|
+
|
46
|
+
The `Toolchain::Attributes` module provides your class with the `attribute` class method which you can use to define rich attributes. Think of it as `attr_accessor` but with type-definitions, optional defaults and a few convenience methods.
|
47
|
+
|
48
|
+
```rb
|
49
|
+
require "toolchain/attributes"
|
50
|
+
|
51
|
+
class Person
|
52
|
+
include Toolchain::Attributes
|
53
|
+
|
54
|
+
attribute :name, String
|
55
|
+
attribute :cash, Integer
|
56
|
+
attribute :birthdate, DateTime
|
57
|
+
attribute :pocket, Array
|
58
|
+
attribute :misc, Hash
|
59
|
+
attribute :winning, Boolean, true
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
##### Defined attributes (keys)
|
64
|
+
|
65
|
+
You can get a list of defined attributes usind the `Person.keys` method.
|
66
|
+
|
67
|
+
##### All attributes/values as a Hash
|
68
|
+
|
69
|
+
You can access a Hash of attributes by calling the `person.attributes` instance method. By default this Hash returns all attributes with Symbol keys (including nested Hashes stored in Hash-type attributes).
|
70
|
+
|
71
|
+
Hash-type attributes automatically convert String-type keys to Symbol-type keys when setting a new Hash.
|
72
|
+
|
73
|
+
##### (Global) Configuration
|
74
|
+
|
75
|
+
You can configure whether you want to have all your attribute keys as String- or Symbol type like so:
|
76
|
+
|
77
|
+
```rb
|
78
|
+
Toolchain::Attributes::Configuration.configure do |config|
|
79
|
+
config.hash_transformation = :stringify_keys # defaults to :symbolize_keys
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
##### Mass-assignment
|
84
|
+
|
85
|
+
You can mass-assign attributes using the `person.attributes` instance method. Note that any attributes you mass-assign that you haven't defined on the model are simply ignored and will not be set. Defined attributes automatically act as a whitelist for what can and cannot be mass-assigned.
|
86
|
+
|
87
|
+
## Toolchain::Validations
|
88
|
+
|
89
|
+
The `Toolchain::Validations` module provides your class with the `validates` and `validate` class methods which you can use to validate attributes. Note that this **DOES NOT** require `Toolchain::Attributes`, any instance method that returns a value can be validated.
|
90
|
+
|
91
|
+
```rb
|
92
|
+
require "toolchain/validations"
|
93
|
+
|
94
|
+
class Person
|
95
|
+
include Toolchain::Validations
|
96
|
+
|
97
|
+
validates :name, presence: true
|
98
|
+
validates :email, email: { message: "isn't valid" }
|
99
|
+
validate :domain_validation
|
100
|
+
|
101
|
+
attr_accessor :name
|
102
|
+
attr_accessor :domain
|
103
|
+
|
104
|
+
def email; @email end
|
105
|
+
def email=(value); @email = value end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def domain_validation
|
110
|
+
if true # host validation logic here
|
111
|
+
errors.add(:domain, "couldn't connect")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
##### Nested Validations
|
118
|
+
|
119
|
+
While `Toolchain::Validations` mirrors `ActiveModel::Validations` in most cases for the sake of consistency, there is an important diference when defining a validation using the `validates` method. In `Toolchain::Validation`, this creates a nested (Hash) validation:
|
120
|
+
|
121
|
+
```rb
|
122
|
+
require "toolchain/validations"
|
123
|
+
|
124
|
+
class Person
|
125
|
+
include Toolchain::Attributes # optional
|
126
|
+
include Toolchain::Validations
|
127
|
+
|
128
|
+
attribute :info, Hash, {}
|
129
|
+
validates :info, :phone, presence: true
|
130
|
+
end
|
131
|
+
|
132
|
+
Person.new.tap do |person|
|
133
|
+
person.validate
|
134
|
+
person.errors[:info][:phone] # => ["can't be blank"]
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
This feature isn't limited to single-level validation. You can have an infinitely deep nested Hash and validate each known attribute on it and it'll properly map the attributes/errors 1:1.
|
139
|
+
|
140
|
+
This is useful when you keep a Hash of serialized data in a database, but you still want a hassle-free way of validating it's attributes that doesn't require you to create custom validators and re-invent the wheel just for nested attributes.
|
141
|
+
|
142
|
+
##### Class-level Validators
|
143
|
+
|
144
|
+
Similar to `ActiveModel::Validations` you can create your own class-level validations.
|
145
|
+
|
146
|
+
```rb
|
147
|
+
require "toolchain/validations"
|
148
|
+
|
149
|
+
class Person
|
150
|
+
include Toolchain::Attributes # optional
|
151
|
+
include Toolchain::Validations
|
152
|
+
|
153
|
+
attribute :name, String
|
154
|
+
attribute :info, Hash, {}
|
155
|
+
|
156
|
+
validate :name_validation
|
157
|
+
validate :info_phone_validation
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def name_validation
|
162
|
+
if true # name validation logic here
|
163
|
+
errors.add(:name, "is invalid")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def info_validation
|
168
|
+
if true # info[:phone] validation logic here
|
169
|
+
errors.add(:info, :phone, "not a number")
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
Person.new.tap do |person|
|
175
|
+
person.validate
|
176
|
+
person.errors[:info][:phone] # => ["can't be blank"]
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
##### Custom (re-usable) Validators
|
181
|
+
|
182
|
+
To define a validator, simply create and load a file like this:
|
183
|
+
|
184
|
+
```rb
|
185
|
+
module Toolchain::Validations::Validators
|
186
|
+
class MyValidator < Base
|
187
|
+
|
188
|
+
def validate
|
189
|
+
errors.add(key_path, message || "is invalid") if invalid?
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def invalid?
|
195
|
+
# logic that determines that this value is invalid
|
196
|
+
#
|
197
|
+
# list of instance methods to work with:
|
198
|
+
#
|
199
|
+
# - object
|
200
|
+
# - errors
|
201
|
+
# - key_path
|
202
|
+
# - data
|
203
|
+
# - message
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
###### object
|
210
|
+
|
211
|
+
A reference to the object that contains the attribute which is being validated.
|
212
|
+
|
213
|
+
###### errors
|
214
|
+
|
215
|
+
A reference to the errors object on the `object`. Write potential validation errors to it.
|
216
|
+
|
217
|
+
###### key_path
|
218
|
+
|
219
|
+
An array of one or more keys. Single key means it's a root-level attribute validation. If this includes two or more keys, it means it's a nested validation on a Hash-type attribute.
|
220
|
+
|
221
|
+
You generally don't need to worry about this. Just pass it in to `errors.add`'s first argument and it'll take care of properly assigning the error message at the right level.
|
222
|
+
|
223
|
+
###### data
|
224
|
+
|
225
|
+
This contains the options data that was passed in to the `validates` method.
|
226
|
+
|
227
|
+
For example:
|
228
|
+
|
229
|
+
```rb
|
230
|
+
validates :cash, format: {
|
231
|
+
with: /^\d+$/,
|
232
|
+
message: "not a valid cash format"
|
233
|
+
}
|
234
|
+
|
235
|
+
data[:with] # /^\d+$/
|
236
|
+
data[:message] # "not a valid cash format"
|
237
|
+
```
|
238
|
+
|
239
|
+
This can optionally be used in your validations.
|
240
|
+
|
241
|
+
###### message
|
242
|
+
|
243
|
+
If `data[:message]` is set (see `data` example), the `message` method will be contain it. Else, `message` will return `nil`, in which case you'll to use a default error message.
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
## Contributing
|
248
|
+
|
249
|
+
1. Fork it ( http://github.com/<my-github-username>/toolchain/fork )
|
250
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
251
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
252
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
253
|
+
5. Create new Pull Request
|
254
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/toolchain.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require_relative "version"
|
2
|
+
require_relative "attributes/ext/boolean"
|
3
|
+
|
4
|
+
module Toolchain::Attributes
|
5
|
+
require_relative "attributes/configuration"
|
6
|
+
require_relative "attributes/errors"
|
7
|
+
require_relative "attributes/helpers"
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
# @return [Array<Symbol>] all defined attributes.
|
12
|
+
#
|
13
|
+
def keys
|
14
|
+
@keys ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Defines an attribute on the Class.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# class Company
|
21
|
+
# attribute :name, String, "Unnamed"
|
22
|
+
# attribute :email, String
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @param key [Symbol, String]
|
26
|
+
# @param type [Class]
|
27
|
+
# @param default [Object]
|
28
|
+
#
|
29
|
+
def attribute(key, type, default = nil)
|
30
|
+
type = [TrueClass, FalseClass] if type == Boolean
|
31
|
+
|
32
|
+
if Helpers.invalid_value?(default, type)
|
33
|
+
raise Errors::TypeMismatch,
|
34
|
+
"expected #{self.name}##{key} to have default value " +
|
35
|
+
"of #{type} type, but received #{default.class} (#{default})."
|
36
|
+
end
|
37
|
+
|
38
|
+
keys.push(key.to_sym)
|
39
|
+
|
40
|
+
define_method(key) do
|
41
|
+
value = instance_variable_get("@#{key}")
|
42
|
+
|
43
|
+
if value.nil?
|
44
|
+
new_value =
|
45
|
+
begin
|
46
|
+
if default.kind_of?(Proc)
|
47
|
+
default.call
|
48
|
+
else
|
49
|
+
default.dup
|
50
|
+
end
|
51
|
+
rescue TypeError
|
52
|
+
default
|
53
|
+
end
|
54
|
+
|
55
|
+
send("#{key}=", new_value)
|
56
|
+
else
|
57
|
+
value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
define_method("#{key}=") do |value|
|
62
|
+
if value.kind_of?(String) && type.respond_to?(:parse)
|
63
|
+
value = type.parse(value)
|
64
|
+
end
|
65
|
+
|
66
|
+
if type == [TrueClass, FalseClass] && [0, "0"].include?(value)
|
67
|
+
value = false
|
68
|
+
end
|
69
|
+
|
70
|
+
if type == [TrueClass, FalseClass] && [1, "1"].include?(value)
|
71
|
+
value = true
|
72
|
+
end
|
73
|
+
|
74
|
+
if Helpers.invalid_value?(value, type)
|
75
|
+
raise Errors::TypeMismatch,
|
76
|
+
"#{self.class}##{key} expected #{type} type, " +
|
77
|
+
"but received #{value.class} (#{value})."
|
78
|
+
end
|
79
|
+
|
80
|
+
if value.kind_of?(Hash)
|
81
|
+
transformation = Configuration.hash_transformation
|
82
|
+
value = Helpers.send(transformation, value)
|
83
|
+
end
|
84
|
+
|
85
|
+
instance_variable_set("@#{key}", value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Takes a Proc that contains attribute definitions and applies
|
90
|
+
# that to this class.
|
91
|
+
#
|
92
|
+
# @param attributes [Proc]
|
93
|
+
#
|
94
|
+
def include_attributes(attributes)
|
95
|
+
class_eval(&attributes)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
module InstanceMethods
|
100
|
+
|
101
|
+
# @param attributes [Hash]
|
102
|
+
#
|
103
|
+
def initialize(attributes = {})
|
104
|
+
self.attributes = attributes
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [Hash] keys and values for each defined attribute.
|
108
|
+
#
|
109
|
+
def attributes
|
110
|
+
include_nil = Configuration.include_nil_in_attributes
|
111
|
+
|
112
|
+
attributes = Hash.new.tap do |attrs|
|
113
|
+
Helpers.each_key(self.class) do |key|
|
114
|
+
value = send(key)
|
115
|
+
|
116
|
+
if !value.nil? || (value.nil? && include_nil)
|
117
|
+
attrs[key] = value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
transformation = Configuration.hash_transformation
|
123
|
+
Helpers.send(transformation, attributes)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Mass-assignment for the defined attributes by passing
|
127
|
+
# in a Hash. Non-existing attributes are ignored.
|
128
|
+
#
|
129
|
+
# @param value [Hash]
|
130
|
+
#
|
131
|
+
def attributes=(value)
|
132
|
+
if !value.kind_of?(Hash)
|
133
|
+
raise Errors::InvalidMassAssignment,
|
134
|
+
"Can't mass-assign #{value.class} (#{value}) type " +
|
135
|
+
"to #{self.class}#attributes."
|
136
|
+
end
|
137
|
+
|
138
|
+
value = Helpers.symbolize_keys(value)
|
139
|
+
keys = Array.new.tap do |keys|
|
140
|
+
Helpers.each_key(self.class) { |key| keys << key }
|
141
|
+
keys.uniq!
|
142
|
+
end
|
143
|
+
|
144
|
+
value.each do |key, value|
|
145
|
+
send("#{key}=", value) if keys.include?(key)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.included(base)
|
151
|
+
base.send(:include, InstanceMethods)
|
152
|
+
base.extend(ClassMethods)
|
153
|
+
end
|
154
|
+
end
|