anima 0.1.1 → 0.2.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 +4 -4
- data/.rubocop.yml +6 -0
- data/.travis.yml +18 -4
- data/Changelog.md +15 -0
- data/Gemfile +7 -2
- data/Gemfile.devtools +26 -12
- data/README.md +4 -2
- data/Rakefile +9 -0
- data/anima.gemspec +6 -6
- data/circle.yml +6 -0
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/reek.yml +3 -1
- data/config/rubocop.yml +6 -10
- data/lib/anima.rb +35 -90
- data/lib/anima/attribute.rb +6 -31
- data/lib/anima/builder.rb +130 -0
- data/lib/anima/error.rb +21 -11
- data/lib/anima/update.rb +3 -2
- data/spec/integration/simple_spec.rb +10 -14
- data/spec/unit/anima/attribute_spec.rb +3 -30
- data/spec/unit/anima/error_spec.rb +14 -4
- data/spec/unit/anima/update_spec.rb +2 -0
- data/spec/unit/anima_spec.rb +36 -4
- metadata +13 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 537a7e2e192c0d4b7519c1299b12bec430145e1f
|
4
|
+
data.tar.gz: 2ca3528ae56ef4f7ab304f9a394121ba74f20f28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d989083d89deabafa011dbd874f1f90eab12f5efe36a8e1a472c70dfdbf061109e3786146b11cd59a3d3b4158e6f8187342c494fbc71fbc0f7f2b6de6fe237cb
|
7
|
+
data.tar.gz: 3874ed4c1a4454eee07b86d45dd3ddd718db57afded1332bee13b17a89e87d68ddb74ae8af227071272a1ee701933c724a350c32b17422fddf98c9e83ff9dd55
|
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
@@ -1,11 +1,25 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
2
|
+
cache: bundler
|
3
|
+
bundler_args: --without yard guard benchmarks
|
4
|
+
script: "bundle exec rake ci:metrics"
|
3
5
|
rvm:
|
4
6
|
- 1.9.3
|
5
7
|
- 2.0.0
|
6
|
-
-
|
7
|
-
-
|
8
|
-
- rbx
|
8
|
+
- 2.1.0-rc1
|
9
|
+
- ruby-head
|
10
|
+
- rbx
|
11
|
+
matrix:
|
12
|
+
include:
|
13
|
+
- rvm: jruby-19mode
|
14
|
+
env: JRUBY_OPTS="$JRUBY_OPTS --debug" # for simplecov
|
15
|
+
- rvm: jruby-20mode
|
16
|
+
env: JRUBY_OPTS="$JRUBY_OPTS --debug" # for simplecov
|
17
|
+
- rvm: jruby-head
|
18
|
+
env: JRUBY_OPTS="$JRUBY_OPTS --debug" # for simplecov
|
19
|
+
allow_failures:
|
20
|
+
- rvm: 2.1.0-rc1
|
21
|
+
- rvm: ruby-head
|
22
|
+
fast_finish: true
|
9
23
|
notifications:
|
10
24
|
irc:
|
11
25
|
channels:
|
data/Changelog.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# v0.2.0 2014-01-13
|
2
|
+
|
3
|
+
Breaking changes:
|
4
|
+
|
5
|
+
* Remove AnimaInfectedClass.attributes_hash(instance)
|
6
|
+
* Replace with AnimaInfectedClass#to_h
|
7
|
+
|
8
|
+
# v0.1.1 2013-09-08
|
9
|
+
|
10
|
+
* [change] Refactor internals.
|
11
|
+
|
12
|
+
# v0.1.0 2013-09-08
|
13
|
+
|
14
|
+
* [change] Update dependencies
|
15
|
+
|
1
16
|
# v0.0.6 2013-02-18
|
2
17
|
|
3
18
|
* [add] Support for updates via Anima::Update mixin
|
data/Gemfile
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
gemspec
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
group :development, :test do
|
8
|
+
gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
|
9
|
+
end
|
10
|
+
|
11
|
+
eval_gemfile 'Gemfile.devtools'
|
data/Gemfile.devtools
CHANGED
@@ -4,21 +4,24 @@ group :development do
|
|
4
4
|
gem 'rake', '~> 10.1.0'
|
5
5
|
gem 'rspec', '~> 2.14.1'
|
6
6
|
gem 'yard', '~> 0.8.7'
|
7
|
+
|
8
|
+
platform :rbx do
|
9
|
+
gem 'rubysl-singleton', '~> 2.0.0'
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
group :yard do
|
10
|
-
gem 'kramdown', '~> 1.
|
14
|
+
gem 'kramdown', '~> 1.3.0'
|
11
15
|
end
|
12
16
|
|
13
17
|
group :guard do
|
14
|
-
gem 'guard', '~>
|
15
|
-
gem 'guard-bundler', '~>
|
16
|
-
gem 'guard-rspec', '~>
|
17
|
-
gem 'guard-rubocop', '~> 0.
|
18
|
-
# gem 'guard-mutant', '~> 0.0.1'
|
18
|
+
gem 'guard', '~> 2.2.4'
|
19
|
+
gem 'guard-bundler', '~> 2.0.0'
|
20
|
+
gem 'guard-rspec', '~> 4.2.0'
|
21
|
+
gem 'guard-rubocop', '~> 1.0.0'
|
19
22
|
|
20
23
|
# file system change event handling
|
21
|
-
gem 'listen', '~>
|
24
|
+
gem 'listen', '~> 2.4.0'
|
22
25
|
gem 'rb-fchange', '~> 0.0.6', require: false
|
23
26
|
gem 'rb-fsevent', '~> 0.9.3', require: false
|
24
27
|
gem 'rb-inotify', '~> 0.9.0', require: false
|
@@ -30,18 +33,29 @@ group :guard do
|
|
30
33
|
end
|
31
34
|
|
32
35
|
group :metrics do
|
33
|
-
gem 'coveralls', '~> 0.
|
36
|
+
gem 'coveralls', '~> 0.7.0'
|
34
37
|
gem 'flay', '~> 2.4.0'
|
35
|
-
gem 'flog', '~> 4.
|
38
|
+
gem 'flog', '~> 4.2.0'
|
36
39
|
gem 'reek', '~> 1.3.2'
|
37
|
-
gem 'rubocop', '~> 0.
|
38
|
-
gem 'simplecov', '~> 0.
|
40
|
+
gem 'rubocop', '~> 0.16.0'
|
41
|
+
gem 'simplecov', '~> 0.8.2'
|
39
42
|
gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
|
40
43
|
|
44
|
+
platforms :mri do
|
45
|
+
gem 'mutant', '~> 0.3.4'
|
46
|
+
end
|
47
|
+
|
41
48
|
platforms :ruby_19, :ruby_20 do
|
42
|
-
# gem 'mutant', git: 'https://github.com/mbj/mutant.git'
|
43
49
|
gem 'yard-spellcheck', '~> 0.1.5'
|
44
50
|
end
|
51
|
+
|
52
|
+
platform :rbx do
|
53
|
+
gem 'json', '~> 1.8.1'
|
54
|
+
gem 'racc', '~> 1.4.10'
|
55
|
+
gem 'rubysl-logger', '~> 2.0.0'
|
56
|
+
gem 'rubysl-open-uri', '~> 2.0.0'
|
57
|
+
gem 'rubysl-prettyprint', '~> 2.0.2'
|
58
|
+
end
|
45
59
|
end
|
46
60
|
|
47
61
|
group :benchmarks do
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@ anima
|
|
5
5
|
[](https://gemnasium.com/mbj/anima)
|
6
6
|
[](https://codeclimate.com/github/mbj/anima)
|
7
7
|
|
8
|
-
Simple library to declare read only attributes on value-objects that are initialized via attributes hash.
|
8
|
+
Simple library to declare read only attributes on value-objects that are initialized via attributes hash.
|
9
9
|
|
10
10
|
Installation
|
11
11
|
------------
|
@@ -59,13 +59,15 @@ class Person
|
|
59
59
|
end
|
60
60
|
|
61
61
|
d = b.update(
|
62
|
-
:salutation => 'Mrs',
|
62
|
+
:salutation => 'Mrs',
|
63
63
|
:firstname => 'Sue',
|
64
64
|
)
|
65
65
|
|
66
66
|
# It returns copies, no inplace modification
|
67
67
|
d.equal?(b) # => false
|
68
68
|
|
69
|
+
# Hash representation
|
70
|
+
d.to_h # => { :salutation => 'Mrs', :firstname => 'Sue' }
|
69
71
|
```
|
70
72
|
|
71
73
|
Credits
|
data/Rakefile
CHANGED
@@ -1,2 +1,11 @@
|
|
1
1
|
require 'devtools'
|
2
2
|
Devtools.init_rake_tasks
|
3
|
+
|
4
|
+
Rake.application.load_imports
|
5
|
+
task('metrics:mutant').clear
|
6
|
+
|
7
|
+
namespace :metrics do
|
8
|
+
task :mutant => :coverage do
|
9
|
+
$stderr.puts 'Anima is a dependency of mutant and zombification is currently defunkt :('
|
10
|
+
end
|
11
|
+
end
|
data/anima.gemspec
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'anima'
|
5
|
-
s.version = '0.
|
5
|
+
s.version = '0.2.0'
|
6
6
|
|
7
7
|
s.authors = ['Markus Schirp']
|
8
8
|
s.email = 'mbj@schirp-dso.com'
|
9
|
-
s.summary = '
|
9
|
+
s.summary = 'Initialize object attributes via attributes hash'
|
10
10
|
s.homepage = 'http://github.com/mbj/anima'
|
11
11
|
s.license = 'MIT'
|
12
12
|
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.require_paths = %w(lib)
|
16
16
|
s.extra_rdoc_files = %w(README.md)
|
17
17
|
|
18
|
-
s.add_dependency('adamantium', '~> 0.1
|
19
|
-
s.add_dependency('equalizer', '~> 0.0.
|
20
|
-
s.add_dependency('abstract_type', '~> 0.0.
|
18
|
+
s.add_dependency('adamantium', '~> 0.1')
|
19
|
+
s.add_dependency('equalizer', '~> 0.0.8')
|
20
|
+
s.add_dependency('abstract_type', '~> 0.0.7')
|
21
21
|
end
|
data/circle.yml
ADDED
data/config/flay.yml
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
3
|
-
total_score:
|
2
|
+
threshold: 6
|
3
|
+
total_score: 44
|
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 9.2
|
data/config/reek.yml
CHANGED
@@ -8,7 +8,7 @@ UncommunicativeParameterName:
|
|
8
8
|
- !ruby/regexp /[0-9]$/
|
9
9
|
- !ruby/regexp /[A-Z]/
|
10
10
|
TooManyMethods:
|
11
|
-
max_methods:
|
11
|
+
max_methods: 9
|
12
12
|
exclude: []
|
13
13
|
enabled: true
|
14
14
|
max_instance_variables: 2
|
@@ -49,6 +49,8 @@ NestedIterators:
|
|
49
49
|
ignore_iterators: []
|
50
50
|
exclude:
|
51
51
|
- Anima::Attribute#define_reader # 2 levels
|
52
|
+
- Anima::Builder#define_anima_method # 2 levels
|
53
|
+
- Anima::Builder#define_attribute_readers # 2 levels
|
52
54
|
enabled: true
|
53
55
|
max_allowed_nesting: 1
|
54
56
|
TooManyStatements:
|
data/config/rubocop.yml
CHANGED
@@ -1,12 +1,4 @@
|
|
1
|
-
|
2
|
-
Includes:
|
3
|
-
- '../**/*.rake'
|
4
|
-
- 'Gemfile'
|
5
|
-
- 'Gemfile.devtools'
|
6
|
-
- 'mutant.gemspec'
|
7
|
-
Excludes:
|
8
|
-
- '**/vendor/**'
|
9
|
-
- '**/benchmarks/**'
|
1
|
+
inherit_from: ../.rubocop.yml
|
10
2
|
|
11
3
|
# Avoid parameter lists longer than five parameters.
|
12
4
|
ParameterLists:
|
@@ -31,7 +23,7 @@ CollectionMethods:
|
|
31
23
|
# sections of code and visually separate them. When the keyword is at the same
|
32
24
|
# level I think it sort of blends in with the def keywords and makes it harder
|
33
25
|
# to scan the code and see where the sections are.
|
34
|
-
|
26
|
+
AccessModifierIndentation:
|
35
27
|
Enabled: false
|
36
28
|
|
37
29
|
MethodLength:
|
@@ -83,3 +75,7 @@ TrivialAccessors:
|
|
83
75
|
# And also have a differend opinion here
|
84
76
|
AndOr:
|
85
77
|
Enabled: false
|
78
|
+
|
79
|
+
# Allow empty lines around body
|
80
|
+
EmptyLinesAroundBody:
|
81
|
+
Enabled: false
|
data/lib/anima.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'adamantium'
|
2
4
|
require 'equalizer'
|
3
5
|
require 'abstract_type'
|
@@ -8,7 +10,7 @@ class Anima < Module
|
|
8
10
|
|
9
11
|
# Return names
|
10
12
|
#
|
11
|
-
# @return [
|
13
|
+
# @return [AttributeSet]
|
12
14
|
#
|
13
15
|
# @api private
|
14
16
|
#
|
@@ -21,35 +23,35 @@ class Anima < Module
|
|
21
23
|
# @api private
|
22
24
|
#
|
23
25
|
def initialize(*names)
|
24
|
-
@attributes = names.uniq.map
|
26
|
+
@attributes = names.uniq.map(&Attribute.method(:new)).freeze
|
25
27
|
end
|
26
28
|
|
27
|
-
# Return new anima with attributes
|
29
|
+
# Return new anima with attributes added
|
28
30
|
#
|
29
31
|
# @return [Anima]
|
30
32
|
#
|
31
33
|
# @example
|
32
|
-
# anima = Anima.new(:foo)
|
34
|
+
# anima = Anima.new(:foo, :bar)
|
33
35
|
# anima.add(:foo) # equals Anima.new(:foo, :bar)
|
34
36
|
#
|
35
|
-
# @api
|
37
|
+
# @api private
|
36
38
|
#
|
37
|
-
def
|
38
|
-
new(attribute_names
|
39
|
+
def add(*names)
|
40
|
+
new(attribute_names + names)
|
39
41
|
end
|
40
42
|
|
41
|
-
# Return new anima with attributes
|
43
|
+
# Return new anima with attributes removed
|
42
44
|
#
|
43
45
|
# @return [Anima]
|
44
46
|
#
|
45
47
|
# @example
|
46
|
-
# anima = Anima.new(:foo
|
48
|
+
# anima = Anima.new(:foo)
|
47
49
|
# anima.remove(:bar) # equals Anima.new(:foo)
|
48
50
|
#
|
49
|
-
# @api
|
51
|
+
# @api public
|
50
52
|
#
|
51
|
-
def
|
52
|
-
new(attribute_names
|
53
|
+
def remove(*names)
|
54
|
+
new(attribute_names - names)
|
53
55
|
end
|
54
56
|
|
55
57
|
# Return attributes hash for instance
|
@@ -88,20 +90,14 @@ class Anima < Module
|
|
88
90
|
# @api private
|
89
91
|
#
|
90
92
|
def initialize_instance(object, attribute_hash)
|
93
|
+
assert_known_attributes(object, attribute_hash)
|
91
94
|
attributes.each do |attribute|
|
92
95
|
attribute.load(object, attribute_hash)
|
93
96
|
end
|
94
|
-
|
95
|
-
overflow = attribute_hash.keys - attribute_names
|
96
|
-
|
97
|
-
unless overflow.empty?
|
98
|
-
raise Error::Unknown.new(object.class, overflow)
|
99
|
-
end
|
100
|
-
|
101
97
|
self
|
102
98
|
end
|
103
99
|
|
104
|
-
private
|
100
|
+
private
|
105
101
|
|
106
102
|
# Hook called when module is included
|
107
103
|
#
|
@@ -111,97 +107,46 @@ private
|
|
111
107
|
#
|
112
108
|
# @api private
|
113
109
|
#
|
114
|
-
def included(
|
115
|
-
|
116
|
-
define_initializer(scope)
|
117
|
-
define_attribute_readers(scope)
|
118
|
-
define_attribute_hash_reader(scope)
|
119
|
-
define_equalizer(scope)
|
110
|
+
def included(descendant)
|
111
|
+
Builder.call(self, descendant)
|
120
112
|
end
|
121
113
|
|
122
|
-
#
|
114
|
+
# Fail unless keys in +attribute_hash+ matches #attribute_names
|
123
115
|
#
|
124
|
-
# @param [
|
125
|
-
#
|
126
|
-
# @return [Anima]
|
127
|
-
#
|
128
|
-
# @api private
|
129
|
-
#
|
130
|
-
def new(attributes)
|
131
|
-
self.class.new(*attributes)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Define anima method on scope
|
116
|
+
# @param [Object] object
|
117
|
+
# the object being initialized
|
135
118
|
#
|
136
|
-
# @param [
|
119
|
+
# @param [Hash] attribute_hash
|
120
|
+
# the attributes to initialize +object+ with
|
137
121
|
#
|
138
122
|
# @return [undefined]
|
139
123
|
#
|
140
|
-
# @
|
141
|
-
#
|
142
|
-
def define_anima_method(scope)
|
143
|
-
anima = self
|
144
|
-
|
145
|
-
scope.define_singleton_method(:anima) do
|
146
|
-
anima
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
# Define equalizer on scope
|
151
|
-
#
|
152
|
-
# @param [Class, Module] scope
|
153
|
-
#
|
154
|
-
# @return [undefined]
|
124
|
+
# @raise [Error::Unknown]
|
155
125
|
#
|
156
126
|
# @api private
|
157
|
-
|
158
|
-
|
159
|
-
scope.send(:include, Equalizer.new(*attribute_names))
|
160
|
-
end
|
127
|
+
def assert_known_attributes(object, attribute_hash)
|
128
|
+
overflow = attribute_hash.keys - attribute_names
|
161
129
|
|
162
|
-
|
163
|
-
|
164
|
-
# @param [Class, Module] scope
|
165
|
-
#
|
166
|
-
# @return [undefined]
|
167
|
-
#
|
168
|
-
# @api private
|
169
|
-
#
|
170
|
-
def define_attribute_readers(scope)
|
171
|
-
attributes.each do |attribute|
|
172
|
-
attribute.define_reader(scope)
|
130
|
+
if overflow.any?
|
131
|
+
fail Error::Unknown.new(object, overflow)
|
173
132
|
end
|
174
133
|
end
|
175
134
|
|
176
|
-
#
|
135
|
+
# Return new instance
|
177
136
|
#
|
178
|
-
# @param [
|
137
|
+
# @param [Enumerable<Symbol>] attributes
|
179
138
|
#
|
180
|
-
# @return [
|
139
|
+
# @return [Anima]
|
181
140
|
#
|
182
141
|
# @api private
|
183
142
|
#
|
184
|
-
def
|
185
|
-
|
186
|
-
self.class.anima.initialize_instance(self, attributes)
|
187
|
-
end
|
143
|
+
def new(attributes)
|
144
|
+
self.class.new(*attributes)
|
188
145
|
end
|
189
146
|
|
190
|
-
|
191
|
-
#
|
192
|
-
# @param [Class, Module] scope
|
193
|
-
#
|
194
|
-
# @return [undefined]
|
195
|
-
#
|
196
|
-
# @api private
|
197
|
-
#
|
198
|
-
def define_attribute_hash_reader(scope)
|
199
|
-
scope.define_singleton_method(:attributes_hash) do |object|
|
200
|
-
anima.attributes_hash(object)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
147
|
+
end # Anima
|
204
148
|
|
149
|
+
require 'anima/builder'
|
205
150
|
require 'anima/error'
|
206
151
|
require 'anima/attribute'
|
207
152
|
require 'anima/update'
|
data/lib/anima/attribute.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
class Anima
|
2
4
|
|
3
5
|
# An attribute
|
@@ -11,7 +13,7 @@ class Anima
|
|
11
13
|
# @api private
|
12
14
|
#
|
13
15
|
def initialize(name)
|
14
|
-
@name = name
|
16
|
+
@name, @instance_variable_name = name, :"@#{name}"
|
15
17
|
end
|
16
18
|
|
17
19
|
# Return attribute name
|
@@ -32,10 +34,8 @@ class Anima
|
|
32
34
|
# @api private
|
33
35
|
#
|
34
36
|
def load(object, attributes)
|
35
|
-
|
36
|
-
|
37
|
-
value = attributes.fetch(attribute_name) do
|
38
|
-
raise Error::Missing.new(object.class, attribute_name)
|
37
|
+
value = attributes.fetch(name) do |attribute_name|
|
38
|
+
fail Error::Missing.new(object, attribute_name)
|
39
39
|
end
|
40
40
|
|
41
41
|
set(object, value)
|
@@ -63,33 +63,8 @@ class Anima
|
|
63
63
|
# @api private
|
64
64
|
#
|
65
65
|
def set(object, value)
|
66
|
-
object.instance_variable_set(instance_variable_name, value)
|
67
|
-
|
68
|
-
self
|
69
|
-
end
|
70
|
-
|
71
|
-
# Return instance variable name
|
72
|
-
#
|
73
|
-
# @return [Symbol]
|
74
|
-
# returns @ prefixed name
|
75
|
-
#
|
76
|
-
# @api private
|
77
|
-
#
|
78
|
-
def instance_variable_name
|
79
|
-
:"@#{name}"
|
80
|
-
end
|
81
|
-
memoize :instance_variable_name
|
66
|
+
object.instance_variable_set(@instance_variable_name, value)
|
82
67
|
|
83
|
-
# Define reader
|
84
|
-
#
|
85
|
-
# @param [Class, Module] scope
|
86
|
-
#
|
87
|
-
# @return [self]
|
88
|
-
#
|
89
|
-
# @api private
|
90
|
-
#
|
91
|
-
def define_reader(scope)
|
92
|
-
scope.send(:attr_reader, name)
|
93
68
|
self
|
94
69
|
end
|
95
70
|
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Anima
|
4
|
+
|
5
|
+
# Builds an anima infected class
|
6
|
+
class Builder
|
7
|
+
|
8
|
+
# Static instance methods for anima infected classes
|
9
|
+
module Methods
|
10
|
+
# Initialize an anima infected object
|
11
|
+
#
|
12
|
+
# @param [#to_h] attributes
|
13
|
+
# a hash that matches anima defined attributes
|
14
|
+
#
|
15
|
+
# @return [undefined]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def initialize(attributes)
|
19
|
+
self.class.anima.initialize_instance(self, attributes)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return a hash representation of an anima infected object
|
23
|
+
#
|
24
|
+
# @return [Hash]
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def to_h
|
28
|
+
self.class.anima.attributes_hash(self)
|
29
|
+
end
|
30
|
+
end # Methods
|
31
|
+
|
32
|
+
include Adamantium::Flat
|
33
|
+
|
34
|
+
# Infect +descendant+ with anima
|
35
|
+
#
|
36
|
+
# @param [Anima] anima
|
37
|
+
# the anima instance used for infection
|
38
|
+
#
|
39
|
+
# @param [Class, Module] descendant
|
40
|
+
# the object to infect
|
41
|
+
#
|
42
|
+
# @return [undefined]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def self.call(anima, descendant)
|
46
|
+
new(anima, descendant).call
|
47
|
+
end
|
48
|
+
|
49
|
+
# Initialize a new instance
|
50
|
+
#
|
51
|
+
# @param [Anima] anima
|
52
|
+
# the anima instance used for infection
|
53
|
+
#
|
54
|
+
# @param [Class, Module] descendant
|
55
|
+
# the object to infect
|
56
|
+
#
|
57
|
+
# @return [undefined]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
def initialize(anima, descendant)
|
61
|
+
@anima = anima
|
62
|
+
@names = anima.attribute_names
|
63
|
+
@descendant = descendant
|
64
|
+
end
|
65
|
+
|
66
|
+
# Infect the instance with anima
|
67
|
+
#
|
68
|
+
# @return [undefined]
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
def call
|
72
|
+
define_equalizer
|
73
|
+
define_anima_method
|
74
|
+
define_attribute_readers
|
75
|
+
descendant_exec do
|
76
|
+
include Methods
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Define equalizer
|
83
|
+
#
|
84
|
+
# @return [undefined]
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def define_equalizer
|
88
|
+
descendant_exec(@names) do |names|
|
89
|
+
include Equalizer.new(*names)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Define anima method
|
94
|
+
#
|
95
|
+
# @return [undefined]
|
96
|
+
#
|
97
|
+
# @api private
|
98
|
+
def define_anima_method
|
99
|
+
descendant_exec(@anima) do |anima|
|
100
|
+
define_singleton_method(:anima) { anima }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Define attribute readers
|
105
|
+
#
|
106
|
+
# @return [undefined]
|
107
|
+
#
|
108
|
+
# @api private
|
109
|
+
def define_attribute_readers
|
110
|
+
descendant_exec(@names) do |names|
|
111
|
+
attr_reader(*names)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Deduplicate instance_exec inside descendant's scope
|
116
|
+
#
|
117
|
+
# @param [*] *args
|
118
|
+
# arguments to pass to instance_exec
|
119
|
+
#
|
120
|
+
# @param [Proc] &block
|
121
|
+
# the block to eval
|
122
|
+
#
|
123
|
+
# @return [undefined]
|
124
|
+
#
|
125
|
+
# @api private
|
126
|
+
def descendant_exec(*args, &block)
|
127
|
+
@descendant.instance_exec(*args, &block)
|
128
|
+
end
|
129
|
+
end # Builder
|
130
|
+
end # Anima
|
data/lib/anima/error.rb
CHANGED
@@ -1,29 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
class Anima
|
2
4
|
|
3
5
|
# Abstract base class for anima errors
|
4
6
|
class Error < RuntimeError
|
7
|
+
|
8
|
+
# Error for unknown attributes
|
9
|
+
class Unknown < self
|
10
|
+
end
|
11
|
+
|
12
|
+
# Error for missing attributes
|
13
|
+
class Missing < self
|
14
|
+
end
|
15
|
+
|
5
16
|
include AbstractType
|
6
17
|
|
18
|
+
DOUBLE_COLON = '::'.freeze
|
19
|
+
|
7
20
|
# Initialize object
|
8
21
|
#
|
9
|
-
# @param [
|
22
|
+
# @param [Object] object
|
10
23
|
# @param [Enumerable<Symbol>] names
|
11
24
|
#
|
12
25
|
# @return [undefined]
|
13
26
|
#
|
14
27
|
# @api private
|
15
28
|
#
|
16
|
-
def initialize(
|
17
|
-
super("#{
|
29
|
+
def initialize(object, names)
|
30
|
+
super("#{name} attribute(s) #{names.inspect} for #{object.class}")
|
18
31
|
end
|
19
32
|
|
20
|
-
|
21
|
-
class Unknown < self
|
22
|
-
end
|
33
|
+
private
|
23
34
|
|
24
|
-
|
25
|
-
|
35
|
+
def name
|
36
|
+
self.class.name.split(DOUBLE_COLON).last
|
26
37
|
end
|
27
|
-
|
28
|
-
|
29
|
-
end
|
38
|
+
end # Error
|
39
|
+
end # Anima
|
data/lib/anima/update.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
class Anima
|
2
4
|
# Module for mixin in update functionallity to anima infected clases
|
3
5
|
module Update
|
@@ -21,8 +23,7 @@ class Anima
|
|
21
23
|
# @api private
|
22
24
|
#
|
23
25
|
def update(attributes)
|
24
|
-
|
25
|
-
klass.new(klass.attributes_hash(self).merge(attributes))
|
26
|
+
self.class.new(to_h.update(attributes))
|
26
27
|
end
|
27
28
|
|
28
29
|
end # Update
|
@@ -1,19 +1,15 @@
|
|
1
|
-
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
subject { class_under_test.new(attributes) }
|
3
|
+
require 'spec_helper'
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
class TestClass
|
6
|
+
include Anima.new(:firstname, :lastname)
|
7
|
+
end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
9
|
+
describe Anima, 'simple integration' do
|
10
|
+
subject { TestClass.new(attributes) }
|
15
11
|
|
16
|
-
context 'when
|
12
|
+
context 'when instantiated with all attributes' do
|
17
13
|
let(:attributes) do
|
18
14
|
{
|
19
15
|
firstname: 'Markus',
|
@@ -25,7 +21,7 @@ describe Anima, 'simple integration' do
|
|
25
21
|
its(:lastname) { should eql('Schirp') }
|
26
22
|
end
|
27
23
|
|
28
|
-
context 'with
|
24
|
+
context 'with instantiated with extra attributes' do
|
29
25
|
let(:attributes) do
|
30
26
|
{
|
31
27
|
firstname: 'Markus',
|
@@ -42,7 +38,7 @@ describe Anima, 'simple integration' do
|
|
42
38
|
end
|
43
39
|
end
|
44
40
|
|
45
|
-
context 'when
|
41
|
+
context 'when instantiated with missing attributes' do
|
46
42
|
|
47
43
|
let(:attributes) { {} }
|
48
44
|
|
@@ -1,29 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Anima::Attribute do
|
4
6
|
let(:object) { described_class.new(:foo) }
|
5
7
|
|
6
|
-
describe '#define_reader' do
|
7
|
-
subject { object.define_reader(target_class) }
|
8
|
-
|
9
|
-
let(:target_class) do
|
10
|
-
Class.new do
|
11
|
-
def initialize(foo)
|
12
|
-
@foo = foo
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
let(:value) { double('Value') }
|
18
|
-
|
19
|
-
it 'should create a reader' do
|
20
|
-
instance = target_class.new(value)
|
21
|
-
-> { subject }.should change { instance.respond_to?(:foo) }.from(false).to(true)
|
22
|
-
end
|
23
|
-
|
24
|
-
it_should_behave_like 'a command method'
|
25
|
-
end
|
26
|
-
|
27
8
|
describe '#get' do
|
28
9
|
subject { object.get(target) }
|
29
10
|
|
@@ -45,14 +26,6 @@ describe Anima::Attribute do
|
|
45
26
|
end
|
46
27
|
end
|
47
28
|
|
48
|
-
describe '#instance_variable_name' do
|
49
|
-
subject { Anima::Attribute.new(:foo).instance_variable_name }
|
50
|
-
|
51
|
-
it { should be(:@foo) }
|
52
|
-
|
53
|
-
it_should_behave_like 'an idempotent method'
|
54
|
-
end
|
55
|
-
|
56
29
|
describe '#load' do
|
57
30
|
subject { object.load(target, attribute_hash) }
|
58
31
|
|
@@ -76,7 +49,7 @@ describe Anima::Attribute do
|
|
76
49
|
let(:attribute_hash) { {} }
|
77
50
|
|
78
51
|
it 'should raise error' do
|
79
|
-
expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target
|
52
|
+
expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target, :foo).message)
|
80
53
|
end
|
81
54
|
end
|
82
55
|
end
|
@@ -1,15 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Anima::Error, '#message' do
|
4
|
-
let(:object) {
|
6
|
+
let(:object) { error.new(instance, name) }
|
7
|
+
|
8
|
+
let(:error) do
|
9
|
+
Class.new(described_class) do
|
10
|
+
def self.name
|
11
|
+
'Test::Error'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
5
15
|
|
6
16
|
subject { object.message }
|
7
17
|
|
8
|
-
let(:
|
9
|
-
let(:name)
|
18
|
+
let(:instance) { Object.new }
|
19
|
+
let(:name) { 'foo' }
|
10
20
|
|
11
21
|
it 'should return the message string' do
|
12
|
-
should eql('Error attribute(s) "
|
22
|
+
should eql('Error attribute(s) "foo" for Object')
|
13
23
|
end
|
14
24
|
|
15
25
|
it_should_behave_like 'an idempotent method'
|
data/spec/unit/anima_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Anima do
|
@@ -80,13 +82,14 @@ describe Anima do
|
|
80
82
|
its(:foo) { should be(value) }
|
81
83
|
|
82
84
|
it { should eql(instance_b) }
|
85
|
+
it { should_not eql(instance_c) }
|
83
86
|
end
|
84
87
|
|
85
88
|
context 'on singleton' do
|
86
89
|
subject { target }
|
87
90
|
|
88
91
|
it 'should define attribute hash reader' do
|
89
|
-
|
92
|
+
instance.to_h.should eql(foo: value)
|
90
93
|
end
|
91
94
|
|
92
95
|
its(:anima) { should be(object) }
|
@@ -114,11 +117,11 @@ describe Anima do
|
|
114
117
|
it_should_behave_like 'a command method'
|
115
118
|
end
|
116
119
|
|
117
|
-
context 'when extra key is
|
120
|
+
context 'when an extra key is present in attribute hash' do
|
118
121
|
let(:attribute_hash) { { foo: foo, bar: bar, baz: double('Baz') } }
|
119
122
|
|
120
123
|
it 'should raise error' do
|
121
|
-
expect { subject }.to raise_error(Anima::Error::Unknown, Anima::Error::Unknown.new(target
|
124
|
+
expect { subject }.to raise_error(Anima::Error::Unknown, Anima::Error::Unknown.new(target, [:baz]).message)
|
122
125
|
end
|
123
126
|
end
|
124
127
|
|
@@ -126,8 +129,37 @@ describe Anima do
|
|
126
129
|
let(:attribute_hash) { { bar: bar } }
|
127
130
|
|
128
131
|
it 'should raise error' do
|
129
|
-
expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target
|
132
|
+
expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target, :foo).message)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'using super in initialize' do
|
138
|
+
subject { klass.new }
|
139
|
+
|
140
|
+
let(:klass) do
|
141
|
+
Class.new do
|
142
|
+
include Anima.new(:foo)
|
143
|
+
def initialize(attributes = { foo: :bar })
|
144
|
+
super
|
145
|
+
end
|
130
146
|
end
|
131
147
|
end
|
148
|
+
|
149
|
+
its(:foo) { should eql(:bar) }
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#to_h on an anima infected instance' do
|
153
|
+
subject { instance.to_h }
|
154
|
+
|
155
|
+
let(:instance) { klass.new(params) }
|
156
|
+
let(:params) { Hash[foo: :bar] }
|
157
|
+
let(:klass) do
|
158
|
+
Class.new do
|
159
|
+
include Anima.new(:foo)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
it { should eql(params) }
|
132
164
|
end
|
133
165
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: anima
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: adamantium
|
@@ -16,42 +16,42 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.1
|
19
|
+
version: '0.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.1
|
26
|
+
version: '0.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: equalizer
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.0.
|
33
|
+
version: 0.0.8
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.0.
|
40
|
+
version: 0.0.8
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: abstract_type
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.0.
|
47
|
+
version: 0.0.7
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.0.
|
54
|
+
version: 0.0.7
|
55
55
|
description:
|
56
56
|
email: mbj@schirp-dso.com
|
57
57
|
executables: []
|
@@ -61,6 +61,7 @@ extra_rdoc_files:
|
|
61
61
|
files:
|
62
62
|
- .gitignore
|
63
63
|
- .rspec
|
64
|
+
- .rubocop.yml
|
64
65
|
- .travis.yml
|
65
66
|
- Changelog.md
|
66
67
|
- Gemfile
|
@@ -70,6 +71,7 @@ files:
|
|
70
71
|
- Rakefile
|
71
72
|
- TODO
|
72
73
|
- anima.gemspec
|
74
|
+
- circle.yml
|
73
75
|
- config/flay.yml
|
74
76
|
- config/flog.yml
|
75
77
|
- config/mutant.yml
|
@@ -79,6 +81,7 @@ files:
|
|
79
81
|
- config/yardstick.yml
|
80
82
|
- lib/anima.rb
|
81
83
|
- lib/anima/attribute.rb
|
84
|
+
- lib/anima/builder.rb
|
82
85
|
- lib/anima/error.rb
|
83
86
|
- lib/anima/update.rb
|
84
87
|
- spec/integration/simple_spec.rb
|
@@ -107,10 +110,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
110
|
version: '0'
|
108
111
|
requirements: []
|
109
112
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.0.
|
113
|
+
rubygems_version: 2.0.14
|
111
114
|
signing_key:
|
112
115
|
specification_version: 4
|
113
|
-
summary:
|
116
|
+
summary: Initialize object attributes via attributes hash
|
114
117
|
test_files:
|
115
118
|
- spec/integration/simple_spec.rb
|
116
119
|
- spec/spec_helper.rb
|