anima 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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