anima 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 537a7e2e192c0d4b7519c1299b12bec430145e1f
4
- data.tar.gz: 2ca3528ae56ef4f7ab304f9a394121ba74f20f28
3
+ metadata.gz: 4d3d1052ef69a252eba9f680d8918977ebe21957
4
+ data.tar.gz: 95726462b39f8b85f7706c5415369eb0e141e48d
5
5
  SHA512:
6
- metadata.gz: d989083d89deabafa011dbd874f1f90eab12f5efe36a8e1a472c70dfdbf061109e3786146b11cd59a3d3b4158e6f8187342c494fbc71fbc0f7f2b6de6fe237cb
7
- data.tar.gz: 3874ed4c1a4454eee07b86d45dd3ddd718db57afded1332bee13b17a89e87d68ddb74ae8af227071272a1ee701933c724a350c32b17422fddf98c9e83ff9dd55
6
+ metadata.gz: 320e39521765b3c969063d635b90c3fc51927b2ac9f1fb46e4c13b02f56a539fb3bf1d57fd8f05742a7fe2b83143c0b3d91c3b3e037a4b142cb3341c7907f9cb
7
+ data.tar.gz: 73e0f2c5c9dc9901dc0c0ce04b899a3c23c5587ff144794b303e53eff9da2bc13d09ca99ea43ff9600805cc8f3ff58c991ebfe66b1eeb9cf8e9c9a796630e29f
@@ -1,6 +1,6 @@
1
1
  AllCops:
2
- Includes:
2
+ Include:
3
3
  - 'Gemfile'
4
- Excludes:
4
+ Exclude:
5
5
  - 'Gemfile.devtools'
6
- - 'vendor/**'
6
+ - 'vendor/**/*'
@@ -3,26 +3,9 @@ cache: bundler
3
3
  bundler_args: --without yard guard benchmarks
4
4
  script: "bundle exec rake ci:metrics"
5
5
  rvm:
6
- - 1.9.3
7
- - 2.0.0
8
- - 2.1.0-rc1
9
- - ruby-head
10
- - rbx
6
+ - '2.1'
7
+ - '2.2'
11
8
  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
23
- notifications:
24
- irc:
25
- channels:
26
- - irc.freenode.org#rom-rb
27
- on_success: never
28
- on_failure: change
9
+ allowed_failures:
10
+ - jruby # Travis jruby does not run in >= 2.1 ruby yet
11
+ - rbx # segfault
@@ -1,3 +1,17 @@
1
+ # v0.3.0 2015-xx-xx
2
+
3
+ Changes:
4
+
5
+ * Drop support for ruby < 2.1
6
+ * Drop support for Anima::Update
7
+ * Add #with as a replacement that is active by default [#13]
8
+
9
+ # v0.2.1 2014-04-10
10
+
11
+ Changes:
12
+
13
+ * Require ruby version >= 1.9.3 in gemspec.
14
+
1
15
  # v0.2.0 2014-01-13
2
16
 
3
17
  Breaking changes:
data/Gemfile CHANGED
@@ -1,11 +1,9 @@
1
- # encoding: utf-8
2
-
3
1
  source 'https://rubygems.org'
4
2
 
5
3
  gemspec
6
4
 
7
- group :development, :test do
8
- gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
9
- end
10
-
11
- eval_gemfile 'Gemfile.devtools'
5
+ gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
6
+ gem 'mutant', git: 'https://github.com/mbj/mutant.git',
7
+ branch: 'fix/anima-update'
8
+ gem 'morpher', git: 'https://github.com/mbj/morpher.git',
9
+ branch: 'fix/upgrade-anima'
data/README.md CHANGED
@@ -4,6 +4,7 @@ anima
4
4
  [![Build Status](https://secure.travis-ci.org/mbj/anima.png?branch=master)](http://travis-ci.org/mbj/anima)
5
5
  [![Dependency Status](https://gemnasium.com/mbj/anima.png)](https://gemnasium.com/mbj/anima)
6
6
  [![Code Climate](https://codeclimate.com/github/mbj/anima.png)](https://codeclimate.com/github/mbj/anima)
7
+ [![Gem Version](https://img.shields.io/gem/v/anima.svg)](https://rubygems.org/gems/anima)
7
8
 
8
9
  Simple library to declare read only attributes on value-objects that are initialized via attributes hash.
9
10
 
@@ -15,7 +16,9 @@ Install the gem `anima` via your preferred method.
15
16
  Examples
16
17
  --------
17
18
 
18
- ```
19
+ ```ruby
20
+ require 'anima'
21
+
19
22
  # Definition
20
23
  class Person
21
24
  include Anima.new(:salutation, :firstname, :lastname)
@@ -23,28 +26,30 @@ end
23
26
 
24
27
  # Every day operation
25
28
  a = Person.new(
26
- :salutation => 'Mr',
27
- :firstname => 'Markus',
28
- :lastname => 'Schirp'
29
+ salutation: 'Mr',
30
+ firstname: 'Markus',
31
+ lastname: 'Schirp'
29
32
  )
30
33
 
34
+ # Returns expected values
31
35
  a.salutation # => "Mr"
32
36
  a.firstname # => "Markus"
33
37
  a.lastname # => "Schirp"
34
- a.frozen? # => true
38
+ a.frozen? # => false
35
39
 
36
40
  b = Person.new(
37
- :salutation => 'Mr',
38
- :firstname => 'John',
39
- :lastname => 'Doe'
41
+ salutation: 'Mr',
42
+ firstname: 'John',
43
+ lastname: 'Doe'
40
44
  )
41
45
 
42
- a = Person.new(
43
- :salutation => 'Mr',
44
- :firstname => 'Markus',
45
- :lastname => 'Schirp'
46
+ c = Person.new(
47
+ salutation: 'Mr',
48
+ firstname: 'Markus',
49
+ lastname: 'Schirp'
46
50
  )
47
51
 
52
+ # Equality based on attributes
48
53
  a == b # => false
49
54
  a.eql?(b) # => false
50
55
  a.equal?(b) # => false
@@ -53,21 +58,25 @@ a == c # => true
53
58
  a.eql?(c) # => true
54
59
  a.equal?(c) # => false
55
60
 
56
- # Functional updates
57
- class Person
58
- include Anima::Update
59
- end
60
-
61
- d = b.update(
62
- :salutation => 'Mrs',
63
- :firstname => 'Sue',
61
+ # Functional-style updates
62
+ d = b.with(
63
+ salutation: 'Mrs',
64
+ firstname: 'Sue',
64
65
  )
65
66
 
66
67
  # It returns copies, no inplace modification
67
68
  d.equal?(b) # => false
68
69
 
69
70
  # Hash representation
70
- d.to_h # => { :salutation => 'Mrs', :firstname => 'Sue' }
71
+ d.to_h # => { salutation: 'Mrs', firstname: 'Sue', lastname: 'Doe' }
72
+
73
+ # Disallows initialization with incompatible attributes
74
+
75
+ Person.new(
76
+ # :saluatation key missing
77
+ "firstname" => "Markus", # does NOT coerce this by intention
78
+ :lastname => "Schirp"
79
+ ) # raises Anima::Error with message "Person attributes missing: [:salutation, :firstname], unknown: ["firstname"]
71
80
  ```
72
81
 
73
82
  Credits
data/Rakefile CHANGED
@@ -6,6 +6,14 @@ task('metrics:mutant').clear
6
6
 
7
7
  namespace :metrics do
8
8
  task :mutant => :coverage do
9
- $stderr.puts 'Anima is a dependency of mutant and zombification is currently defunkt :('
9
+ system(*%w[
10
+ bundle exec mutant
11
+ --include lib
12
+ --require anima
13
+ --use rspec
14
+ --zombie
15
+ --
16
+ Anima*
17
+ ]) or fail "Mutant task failed"
10
18
  end
11
19
  end
@@ -1,8 +1,8 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  Gem::Specification.new do |s|
4
- s.name = 'anima'
5
- s.version = '0.2.0'
4
+ s.name = 'anima'
5
+ s.version = '0.3.0'
6
6
 
7
7
  s.authors = ['Markus Schirp']
8
8
  s.email = 'mbj@schirp-dso.com'
@@ -15,7 +15,9 @@ 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.8')
18
+ s.required_ruby_version = '>= 2.1.0'
19
+
20
+ s.add_dependency('adamantium', '~> 0.2')
21
+ s.add_dependency('equalizer', '~> 0.0.11')
20
22
  s.add_dependency('abstract_type', '~> 0.0.7')
21
23
  end
data/circle.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  ---
2
- ruby:
3
- version: 2.0.0-p353
2
+ machine:
3
+ ruby:
4
+ version: '2.2'
4
5
  test:
5
6
  override:
6
7
  - bundle exec rake ci
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 6
3
- total_score: 44
3
+ total_score: 29
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 9.2
2
+ threshold: 11.9
@@ -1,3 +1,4 @@
1
1
  ---
2
2
  name: anima
3
3
  namespace: Anima
4
+ zombify: true
@@ -22,7 +22,8 @@ UncommunicativeMethodName:
22
22
  - !ruby/regexp /[A-Z]/
23
23
  LongParameterList:
24
24
  max_params: 2 # TODO: decrease max_params to 2
25
- exclude: []
25
+ exclude:
26
+ - Anima::Error#initialize # 3 params
26
27
  enabled: true
27
28
  overrides: {}
28
29
  FeatureEnvy:
@@ -35,9 +36,10 @@ ClassVariable:
35
36
  BooleanParameter:
36
37
  exclude: []
37
38
  enabled: true
39
+ # Buggy like hell
38
40
  IrresponsibleModule:
39
41
  exclude: []
40
- enabled: true
42
+ enabled: false
41
43
  UncommunicativeModuleName:
42
44
  accept: []
43
45
  exclude: []
@@ -49,8 +51,7 @@ NestedIterators:
49
51
  ignore_iterators: []
50
52
  exclude:
51
53
  - Anima::Attribute#define_reader # 2 levels
52
- - Anima::Builder#define_anima_method # 2 levels
53
- - Anima::Builder#define_attribute_readers # 2 levels
54
+ - Anima#included # 2 levels
54
55
  enabled: true
55
56
  max_allowed_nesting: 1
56
57
  TooManyStatements:
@@ -64,7 +65,8 @@ DuplicateMethodCall:
64
65
  max_calls: 1
65
66
  UtilityFunction:
66
67
  max_helper_calls: 1
67
- exclude: []
68
+ exclude:
69
+ - Anima::Error#initialize # false positive, calls super
68
70
  enabled: true
69
71
  Attribute:
70
72
  exclude: []
@@ -93,3 +95,6 @@ LongYieldList:
93
95
  max_params: 1
94
96
  exclude: []
95
97
  enabled: true
98
+ # Thats the whole point of this lib, oOo.
99
+ ModuleInitialize:
100
+ enabled: false
@@ -16,6 +16,9 @@ CollectionMethods:
16
16
  inject: 'reduce'
17
17
  find: 'detect'
18
18
  find_all: 'select'
19
+ Exclude:
20
+ # The Pathname#find method is mistakenly identified as Enumberable#find
21
+ - lib/tasks/assets_precompile_nodigest.rake
19
22
 
20
23
  # Do not force public/protected/private keyword to be indented at the same
21
24
  # level as the def keyword. My personal preference is to outdent these keywords
@@ -26,38 +29,20 @@ CollectionMethods:
26
29
  AccessModifierIndentation:
27
30
  Enabled: false
28
31
 
29
- MethodLength:
30
- CountComments: false
31
- Max: 17 # TODO: Bring down to 10
32
-
33
- RegexpLiteral: # I do not agree %r(\A) is more readable than /\A/
34
- Enabled: false
35
-
36
- Eval:
37
- Enabled: false # Mutant must use Kernel#eval to inject mutated source
38
-
39
32
  # Limit line length
40
33
  LineLength:
41
- Max: 124 # TODO: lower to 79
34
+ Max: 79
42
35
 
43
36
  # Disable documentation checking until a class needs to be documented once
44
37
  Documentation:
45
38
  Enabled: false
46
39
 
47
- # Do not favor modifier if/unless usage when you have a single-line body
48
- IfUnlessModifier:
49
- Enabled: false
50
-
51
- # Mutant needs to define methods like def bar; end in specs
52
- Semicolon:
53
- Enabled: false
54
-
55
- # Mutant needs to define multiple methods on same line in specs
56
- EmptyLineBetweenDefs:
40
+ # Do not always use &&/|| instead of and/or.
41
+ AndOr:
57
42
  Enabled: false
58
43
 
59
- # Mutant needs to define singleton methods like Foo.bar in specs
60
- ClassMethods:
44
+ # Do not favor modifier if/unless usage when you have a single-line body
45
+ IfUnlessModifier:
61
46
  Enabled: false
62
47
 
63
48
  # Allow case equality operator (in limited use within the specs)
@@ -72,10 +57,81 @@ ConstantName:
72
57
  TrivialAccessors:
73
58
  Enabled: false
74
59
 
75
- # And also have a differend opinion here
76
- AndOr:
60
+ # Allow empty lines around class body
61
+ EmptyLinesAroundClassBody:
62
+ Enabled: false
63
+
64
+ # Allow empty lines around module body
65
+ EmptyLinesAroundModuleBody:
66
+ Enabled: false
67
+
68
+ # Allow empty lines around block body
69
+ EmptyLinesAroundBlockBody:
70
+ Enabled: false
71
+
72
+ # Allow multiple line operations to not require indentation
73
+ MultilineOperationIndentation:
74
+ Enabled: false
75
+
76
+ # Prefer String#% over Kernel#sprintf
77
+ FormatString:
78
+ Enabled: false
79
+
80
+ # Use square brackets for literal Array objects
81
+ PercentLiteralDelimiters:
82
+ PreferredDelimiters:
83
+ '%': '{}'
84
+ '%i': '[]'
85
+ '%q': ()
86
+ '%Q': ()
87
+ '%r': '{}'
88
+ '%s': ()
89
+ '%w': '[]'
90
+ '%W': '[]'
91
+ '%x': ()
92
+
93
+ # Use %i[...] for arrays of symbols
94
+ SymbolArray:
95
+ Enabled: true
96
+
97
+ # Align if/else blocks with the variable assignment
98
+ EndAlignment:
99
+ AlignWith: variable
100
+
101
+ # Do not always align parameters when it is easier to read
102
+ AlignParameters:
103
+ Exclude:
104
+ - spec/**/*_spec.rb
105
+
106
+ # Prefer #kind_of? over #is_a?
107
+ ClassCheck:
108
+ EnforcedStyle: kind_of?
109
+
110
+ # Do not prefer double quotes to be used when %q or %Q is more appropriate
111
+ UnneededPercentQ:
112
+ Enabled: false
113
+
114
+ # Allow a maximum ABC score
115
+ Metrics/AbcSize:
116
+ Max: 21.02
117
+
118
+ # Do not prefer lambda.call(...) over lambda.(...)
119
+ LambdaCall:
120
+ Enabled: false
121
+
122
+ # Buggy cop, returns false positive for our code base
123
+ NonLocalExitFromIterator:
124
+ Enabled: false
125
+
126
+ # To allow alignment of similar expressions we want to allow more than one
127
+ # space around operators:
128
+ #
129
+ # let(:a) { bar + something }
130
+ # let(:b) { foobar + something }
131
+ #
132
+ SpaceAroundOperators:
77
133
  Enabled: false
78
134
 
79
- # Allow empty lines around body
80
- EmptyLinesAroundBody:
135
+ # We use parallel assignments with great success
136
+ ParallelAssignment:
81
137
  Enabled: false
@@ -31,8 +31,8 @@ class Anima < Module
31
31
  # @return [Anima]
32
32
  #
33
33
  # @example
34
- # anima = Anima.new(:foo, :bar)
35
- # anima.add(:foo) # equals Anima.new(:foo, :bar)
34
+ # anima = Anima.new(:foo)
35
+ # anima.add(:bar) # equals Anima.new(:foo, :bar)
36
36
  #
37
37
  # @api private
38
38
  #
@@ -45,7 +45,7 @@ class Anima < Module
45
45
  # @return [Anima]
46
46
  #
47
47
  # @example
48
- # anima = Anima.new(:foo)
48
+ # anima = Anima.new(:foo, :bar)
49
49
  # anima.remove(:bar) # equals Anima.new(:foo)
50
50
  #
51
51
  # @api public
@@ -90,16 +90,62 @@ class Anima < Module
90
90
  # @api private
91
91
  #
92
92
  def initialize_instance(object, attribute_hash)
93
- assert_known_attributes(object, attribute_hash)
93
+ assert_known_attributes(object.class, attribute_hash)
94
94
  attributes.each do |attribute|
95
95
  attribute.load(object, attribute_hash)
96
96
  end
97
97
  self
98
98
  end
99
99
 
100
+ # Static instance methods for anima infected classes
101
+ module InstanceMethods
102
+ # Initialize an anima infected object
103
+ #
104
+ # @param [#to_h] attributes
105
+ # a hash that matches anima defined attributes
106
+ #
107
+ # @return [undefined]
108
+ #
109
+ # @api public
110
+ def initialize(attributes)
111
+ self.class.anima.initialize_instance(self, attributes)
112
+ end
113
+
114
+ # Return a hash representation of an anima infected object
115
+ #
116
+ # @return [Hash]
117
+ #
118
+ # @api public
119
+ def to_h
120
+ self.class.anima.attributes_hash(self)
121
+ end
122
+
123
+ # Return updated instance
124
+ #
125
+ # @example
126
+ # klass = Class.new do
127
+ # include Anima.new(:foo, :bar)
128
+ # end
129
+ #
130
+ # foo = klass.new(:foo => 1, :bar => 2)
131
+ # updated = foo.with(:foo => 3)
132
+ # updated.foo # => 3
133
+ # updated.bar # => 2
134
+ #
135
+ # @param [Hash] attributes
136
+ #
137
+ # @return [Anima]
138
+ #
139
+ # @api private
140
+ #
141
+ def with(attributes)
142
+ self.class.new(to_h.update(attributes))
143
+ end
144
+ end # InstanceMethods
145
+
100
146
  private
101
147
 
102
- # Hook called when module is included
148
+ # Infect the instance with anima
103
149
  #
104
150
  # @param [Class, Module] scope
105
151
  #
@@ -108,28 +154,41 @@ class Anima < Module
108
154
  # @api private
109
155
  #
110
156
  def included(descendant)
111
- Builder.call(self, descendant)
157
+ descendant.instance_exec(self, attribute_names) do |anima, names|
158
+ # Define anima method
159
+ define_singleton_method(:anima) { anima }
160
+
161
+ # Define instance methods
162
+ include InstanceMethods
163
+
164
+ # Define attribute readers
165
+ attr_reader(*names)
166
+
167
+ # Define equalizer
168
+ include Equalizer.new(*names)
169
+ end
112
170
  end
113
171
 
114
172
  # Fail unless keys in +attribute_hash+ matches #attribute_names
115
173
  #
116
- # @param [Object] object
117
- # the object being initialized
174
+ # @param [Class] klass
175
+ # the class being initialized
118
176
  #
119
177
  # @param [Hash] attribute_hash
120
178
  # the attributes to initialize +object+ with
121
179
  #
122
180
  # @return [undefined]
123
181
  #
124
- # @raise [Error::Unknown]
182
+ # @raise [Error]
125
183
  #
126
184
  # @api private
127
- def assert_known_attributes(object, attribute_hash)
128
- overflow = attribute_hash.keys - attribute_names
185
+ def assert_known_attributes(klass, attribute_hash)
186
+ keys = attribute_hash.keys
129
187
 
130
- if overflow.any?
131
- fail Error::Unknown.new(object, overflow)
132
- end
188
+ unknown = keys - attribute_names
189
+ missing = attribute_names - keys
190
+
191
+ fail Error.new(klass, missing, unknown) if unknown.any? || missing.any?
133
192
  end
134
193
 
135
194
  # Return new instance
@@ -143,10 +202,7 @@ class Anima < Module
143
202
  def new(attributes)
144
203
  self.class.new(*attributes)
145
204
  end
146
-
147
205
  end # Anima
148
206
 
149
- require 'anima/builder'
150
207
  require 'anima/error'
151
208
  require 'anima/attribute'
152
- require 'anima/update'
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class Anima
4
-
5
4
  # An attribute
6
5
  class Attribute
7
6
  include Adamantium::Flat, Equalizer.new(:name)
@@ -24,6 +23,14 @@ class Anima
24
23
  #
25
24
  attr_reader :name
26
25
 
26
+ # Return instance variable name
27
+ #
28
+ # @return [Symbol]
29
+ #
30
+ # @api private
31
+ #
32
+ attr_reader :instance_variable_name
33
+
27
34
  # Load attribute
28
35
  #
29
36
  # @param [Object] object
@@ -34,11 +41,7 @@ class Anima
34
41
  # @api private
35
42
  #
36
43
  def load(object, attributes)
37
- value = attributes.fetch(name) do |attribute_name|
38
- fail Error::Missing.new(object, attribute_name)
39
- end
40
-
41
- set(object, value)
44
+ set(object, attributes.fetch(name))
42
45
  end
43
46
 
44
47
  # Get attribute value from object
@@ -63,10 +66,9 @@ class Anima
63
66
  # @api private
64
67
  #
65
68
  def set(object, value)
66
- object.instance_variable_set(@instance_variable_name, value)
69
+ object.instance_variable_set(instance_variable_name, value)
67
70
 
68
71
  self
69
72
  end
70
-
71
73
  end # Attribute
72
74
  end # Anima
@@ -1,39 +1,24 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class Anima
4
-
5
4
  # Abstract base class for anima errors
6
5
  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
-
16
- include AbstractType
17
-
18
- DOUBLE_COLON = '::'.freeze
6
+ FORMAT = '%s attributes missing: %s, unknown: %s'.freeze
7
+ private_constant(*constants(false))
19
8
 
20
9
  # Initialize object
21
10
  #
22
- # @param [Object] object
23
- # @param [Enumerable<Symbol>] names
11
+ # @param [Class] klass
12
+ # the class being initialized
13
+ # @param [Enumerable<Symbol>] missing
14
+ # @param [Enumerable<Symbol>] unknown
24
15
  #
25
16
  # @return [undefined]
26
17
  #
27
18
  # @api private
28
19
  #
29
- def initialize(object, names)
30
- super("#{name} attribute(s) #{names.inspect} for #{object.class}")
31
- end
32
-
33
- private
34
-
35
- def name
36
- self.class.name.split(DOUBLE_COLON).last
20
+ def initialize(klass, missing, unknown)
21
+ super(FORMAT % [klass, missing, unknown])
37
22
  end
38
23
  end # Error
39
24
  end # Anima
@@ -32,20 +32,19 @@ describe Anima, 'simple integration' do
32
32
 
33
33
  it 'should raise error' do
34
34
  expect { subject }.to raise_error(
35
- Anima::Error::Unknown,
36
- 'Unknown attribute(s) [:extra] for TestClass'
35
+ Anima::Error,
36
+ 'TestClass attributes missing: [], unknown: [:extra]'
37
37
  )
38
38
  end
39
39
  end
40
40
 
41
41
  context 'when instantiated with missing attributes' do
42
-
43
42
  let(:attributes) { {} }
44
43
 
45
44
  it 'should raise error' do
46
45
  expect { subject }.to raise_error(
47
- Anima::Error::Missing,
48
- 'Missing attribute(s) :firstname for TestClass'
46
+ Anima::Error,
47
+ 'TestClass attributes missing: [:firstname, :lastname], unknown: []'
49
48
  )
50
49
  end
51
50
  end
@@ -3,6 +3,3 @@
3
3
  require 'anima'
4
4
  require 'devtools'
5
5
  Devtools.init_spec_helper
6
-
7
- RSpec.configure do |config|
8
- end
@@ -29,29 +29,24 @@ describe Anima::Attribute do
29
29
  describe '#load' do
30
30
  subject { object.load(target, attribute_hash) }
31
31
 
32
- let(:target) { Object.new }
33
-
34
- let(:value) { double('Value') }
35
-
36
- context 'when attribute hash contains key' do
37
- let(:attribute_hash) { { foo: value } }
32
+ let(:target) { Object.new }
33
+ let(:value) { double('Value') }
34
+ let(:attribute_hash) { { foo: value } }
38
35
 
39
- it 'should set value as instance variable' do
40
- subject
41
- target.instance_variable_get(:@foo).should be(value)
42
- end
36
+ it 'should set value as instance variable' do
37
+ subject
38
+ expect(target.instance_variable_get(:@foo)).to be(value)
39
+ end
43
40
 
44
- it_should_behave_like 'a command method'
41
+ it_should_behave_like 'a command method'
42
+ end
45
43
 
46
- end
44
+ describe '#instance_variable_name' do
45
+ subject { object.instance_variable_name }
47
46
 
48
- context 'when attribute hash does not contain key' do
49
- let(:attribute_hash) { {} }
47
+ it { should be(:@foo) }
50
48
 
51
- it 'should raise error' do
52
- expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target, :foo).message)
53
- end
54
- end
49
+ it_should_behave_like 'an idempotent method'
55
50
  end
56
51
 
57
52
  describe '#set' do
@@ -65,7 +60,7 @@ describe Anima::Attribute do
65
60
 
66
61
  it 'should set value as instance variable' do
67
62
  subject
68
- target.instance_variable_get(:@foo).should be(value)
63
+ expect(target.instance_variable_get(:@foo)).to be(value)
69
64
  end
70
65
  end
71
66
  end
@@ -2,25 +2,19 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Anima::Error, '#message' do
6
- let(:object) { error.new(instance, name) }
5
+ describe Anima::Error do
6
+ describe '#message' do
7
+ let(:object) { described_class.new(Anima, missing, unknown) }
7
8
 
8
- let(:error) do
9
- Class.new(described_class) do
10
- def self.name
11
- 'Test::Error'
12
- end
13
- end
14
- end
9
+ let(:missing) { %i[missing] }
10
+ let(:unknown) { %i[unknown] }
15
11
 
16
- subject { object.message }
12
+ subject { object.message }
17
13
 
18
- let(:instance) { Object.new }
19
- let(:name) { 'foo' }
14
+ it 'should return the message string' do
15
+ should eql('Anima attributes missing: [:missing], unknown: [:unknown]')
16
+ end
20
17
 
21
- it 'should return the message string' do
22
- should eql('Error attribute(s) "foo" for Object')
18
+ it_should_behave_like 'an idempotent method'
23
19
  end
24
-
25
- it_should_behave_like 'an idempotent method'
26
20
  end
@@ -89,7 +89,7 @@ describe Anima do
89
89
  subject { target }
90
90
 
91
91
  it 'should define attribute hash reader' do
92
- instance.to_h.should eql(foo: value)
92
+ expect(instance.to_h).to eql(foo: value)
93
93
  end
94
94
 
95
95
  its(:anima) { should be(object) }
@@ -109,9 +109,15 @@ describe Anima do
109
109
 
110
110
  it 'should initialize target instance variables' do
111
111
  subject
112
- target.instance_variables.map(&:to_sym).to_set.should eql([:@foo, :@bar].to_set)
113
- target.instance_variable_get(:@foo).should be(foo)
114
- target.instance_variable_get(:@bar).should be(bar)
112
+
113
+ expect(
114
+ target
115
+ .instance_variables
116
+ .map(&:to_sym)
117
+ .to_set
118
+ ).to eql(%i[@foo @bar].to_set)
119
+ expect(target.instance_variable_get(:@foo)).to be(foo)
120
+ expect(target.instance_variable_get(:@bar)).to be(bar)
115
121
  end
116
122
 
117
123
  it_should_behave_like 'a command method'
@@ -121,7 +127,10 @@ describe Anima do
121
127
  let(:attribute_hash) { { foo: foo, bar: bar, baz: double('Baz') } }
122
128
 
123
129
  it 'should raise error' do
124
- expect { subject }.to raise_error(Anima::Error::Unknown, Anima::Error::Unknown.new(target, [:baz]).message)
130
+ expect { subject }.to raise_error(
131
+ Anima::Error,
132
+ Anima::Error.new(target.class, [], [:baz]).message
133
+ )
125
134
  end
126
135
  end
127
136
 
@@ -129,7 +138,9 @@ describe Anima do
129
138
  let(:attribute_hash) { { bar: bar } }
130
139
 
131
140
  it 'should raise error' do
132
- expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target, :foo).message)
141
+ expect { subject }.to raise_error(
142
+ Anima::Error.new(target.class, [:foo], []).message
143
+ )
133
144
  end
134
145
  end
135
146
  end
@@ -162,4 +173,28 @@ describe Anima do
162
173
 
163
174
  it { should eql(params) }
164
175
  end
176
+
177
+ describe '#with' do
178
+ subject { object.with(attributes) }
179
+
180
+ let(:klass) do
181
+ Class.new do
182
+ include Anima.new(:foo, :bar)
183
+ end
184
+ end
185
+
186
+ let(:object) { klass.new(foo: 1, bar: 2) }
187
+
188
+ context 'with empty attributes' do
189
+ let(:attributes) { {} }
190
+
191
+ it { should eql(object) }
192
+ end
193
+
194
+ context 'with updated attribute' do
195
+ let(:attributes) { { foo: 3 } }
196
+
197
+ it { should eql(klass.new(foo: 3, bar: 2)) }
198
+ end
199
+ end
165
200
  end
metadata CHANGED
@@ -1,55 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anima
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.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: 2014-01-13 00:00:00.000000000 Z
11
+ date: 2015-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: adamantium
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.1'
19
+ version: '0.2'
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.2'
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.8
33
+ version: 0.0.11
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.8
40
+ version: 0.0.11
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
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
54
  version: 0.0.7
55
55
  description:
@@ -59,13 +59,12 @@ extensions: []
59
59
  extra_rdoc_files:
60
60
  - README.md
61
61
  files:
62
- - .gitignore
63
- - .rspec
64
- - .rubocop.yml
65
- - .travis.yml
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".rubocop.yml"
65
+ - ".travis.yml"
66
66
  - Changelog.md
67
67
  - Gemfile
68
- - Gemfile.devtools
69
68
  - Guardfile
70
69
  - README.md
71
70
  - Rakefile
@@ -81,14 +80,11 @@ files:
81
80
  - config/yardstick.yml
82
81
  - lib/anima.rb
83
82
  - lib/anima/attribute.rb
84
- - lib/anima/builder.rb
85
83
  - lib/anima/error.rb
86
- - lib/anima/update.rb
87
84
  - spec/integration/simple_spec.rb
88
85
  - spec/spec_helper.rb
89
86
  - spec/unit/anima/attribute_spec.rb
90
87
  - spec/unit/anima/error_spec.rb
91
- - spec/unit/anima/update_spec.rb
92
88
  - spec/unit/anima_spec.rb
93
89
  homepage: http://github.com/mbj/anima
94
90
  licenses:
@@ -100,17 +96,17 @@ require_paths:
100
96
  - lib
101
97
  required_ruby_version: !ruby/object:Gem::Requirement
102
98
  requirements:
103
- - - '>='
99
+ - - ">="
104
100
  - !ruby/object:Gem::Version
105
- version: '0'
101
+ version: 2.1.0
106
102
  required_rubygems_version: !ruby/object:Gem::Requirement
107
103
  requirements:
108
- - - '>='
104
+ - - ">="
109
105
  - !ruby/object:Gem::Version
110
106
  version: '0'
111
107
  requirements: []
112
108
  rubyforge_project:
113
- rubygems_version: 2.0.14
109
+ rubygems_version: 2.4.5.1
114
110
  signing_key:
115
111
  specification_version: 4
116
112
  summary: Initialize object attributes via attributes hash
@@ -119,6 +115,5 @@ test_files:
119
115
  - spec/spec_helper.rb
120
116
  - spec/unit/anima/attribute_spec.rb
121
117
  - spec/unit/anima/error_spec.rb
122
- - spec/unit/anima/update_spec.rb
123
118
  - spec/unit/anima_spec.rb
124
119
  has_rdoc:
@@ -1,69 +0,0 @@
1
- # encoding: utf-8
2
-
3
- group :development do
4
- gem 'rake', '~> 10.1.0'
5
- gem 'rspec', '~> 2.14.1'
6
- gem 'yard', '~> 0.8.7'
7
-
8
- platform :rbx do
9
- gem 'rubysl-singleton', '~> 2.0.0'
10
- end
11
- end
12
-
13
- group :yard do
14
- gem 'kramdown', '~> 1.3.0'
15
- end
16
-
17
- group :guard do
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'
22
-
23
- # file system change event handling
24
- gem 'listen', '~> 2.4.0'
25
- gem 'rb-fchange', '~> 0.0.6', require: false
26
- gem 'rb-fsevent', '~> 0.9.3', require: false
27
- gem 'rb-inotify', '~> 0.9.0', require: false
28
-
29
- # notification handling
30
- gem 'libnotify', '~> 0.8.0', require: false
31
- gem 'rb-notifu', '~> 0.0.4', require: false
32
- gem 'terminal-notifier-guard', '~> 1.5.3', require: false
33
- end
34
-
35
- group :metrics do
36
- gem 'coveralls', '~> 0.7.0'
37
- gem 'flay', '~> 2.4.0'
38
- gem 'flog', '~> 4.2.0'
39
- gem 'reek', '~> 1.3.2'
40
- gem 'rubocop', '~> 0.16.0'
41
- gem 'simplecov', '~> 0.8.2'
42
- gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
43
-
44
- platforms :mri do
45
- gem 'mutant', '~> 0.3.4'
46
- end
47
-
48
- platforms :ruby_19, :ruby_20 do
49
- gem 'yard-spellcheck', '~> 0.1.5'
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
59
- end
60
-
61
- group :benchmarks do
62
- gem 'rbench', '~> 0.2.3'
63
- end
64
-
65
- platform :jruby do
66
- group :jruby do
67
- gem 'jruby-openssl', '~> 0.8.5'
68
- end
69
- end
@@ -1,130 +0,0 @@
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
@@ -1,30 +0,0 @@
1
- # encoding: utf-8
2
-
3
- class Anima
4
- # Module for mixin in update functionallity to anima infected clases
5
- module Update
6
-
7
- # Return updated instance
8
- #
9
- # @example
10
- # klass = Class.new do
11
- # include Anima.new(:foo, :bar), Anima::Update
12
- # end
13
- #
14
- # foo = klass.new(:foo => 1, :bar => 2)
15
- # updated = foo.update(:foo => 3)
16
- # updated.foo # => 3
17
- # updated.bar # => 2
18
- #
19
- # @param [Hash] attributes
20
- #
21
- # @return [Anima]
22
- #
23
- # @api private
24
- #
25
- def update(attributes)
26
- self.class.new(to_h.update(attributes))
27
- end
28
-
29
- end # Update
30
- end # Anima
@@ -1,27 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- describe Anima::Update, '#update' do
6
- subject { object.update(attributes) }
7
-
8
- let(:object) { class_under_test.new(foo: 1, bar: 2) }
9
-
10
- let(:class_under_test) do
11
- Class.new do
12
- include Anima.new(:foo, :bar), Anima::Update
13
- end
14
- end
15
-
16
- context 'with empty attributes' do
17
- let(:attributes) { {} }
18
-
19
- it { should eql(object) }
20
- end
21
-
22
- context 'with updated attribute' do
23
- let(:attributes) { { foo: 3 } }
24
-
25
- it { should eql(class_under_test.new(foo: 3, bar: 2)) }
26
- end
27
- end