anima 0.0.2

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.
@@ -0,0 +1,2 @@
1
+ /.rspec
2
+ /Gemfile.lock
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ bundler_args: --without guard
3
+ script: "bundle exec rake ci"
4
+ rvm:
5
+ - 1.8.7
6
+ - 1.9.2
7
+ - 1.9.3
8
+ - jruby-18mode
9
+ - jruby-19mode
10
+ - rbx-18mode
11
+ - rbx-19mode
12
+ notifications:
13
+ email:
14
+ - mbj@seonic.net
@@ -0,0 +1,3 @@
1
+ # v0.0.2 2012-12-13
2
+
3
+ First public release!
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'devtools', :git => 'https://github.com/datamapper/devtools.git'
6
+ eval File.read('Gemfile.devtools')
@@ -0,0 +1,44 @@
1
+ group :development do
2
+ gem 'rake', '~> 0.9.2'
3
+ gem 'rspec', '~> 2.12.0'
4
+ gem 'yard', '~> 0.8.3'
5
+ end
6
+
7
+ group :guard do
8
+ gem 'guard', '~> 1.5.4'
9
+ gem 'guard-bundler', '~> 1.0.0'
10
+ gem 'guard-rspec', '~> 2.1.1'
11
+ gem 'rb-inotify', :git => 'https://github.com/mbj/rb-inotify'
12
+ end
13
+
14
+ group :benchmarks do
15
+ gem 'rbench', '~> 0.2.3'
16
+ end
17
+
18
+ platform :jruby do
19
+ group :jruby do
20
+ gem 'jruby-openssl', '~> 0.7.4'
21
+ end
22
+ end
23
+
24
+ group :metrics do
25
+ gem 'flay', '~> 1.4.2'
26
+ gem 'flog', '~> 2.5.1'
27
+ gem 'reek', '~> 1.2.8', :git => 'https://github.com/dkubb/reek.git'
28
+ gem 'roodi', '~> 2.1.0'
29
+ gem 'yardstick', '~> 0.8.0'
30
+ gem 'mutant', '~> 0.2.5'
31
+
32
+ platforms :ruby_18, :ruby_19 do
33
+ # this indirectly depends on ffi which does not build on ruby-head
34
+ gem 'yard-spellcheck', '~> 0.1.5'
35
+ end
36
+
37
+ platforms :mri_19 do
38
+ gem 'simplecov', '~> 0.7'
39
+ end
40
+
41
+ platforms :rbx do
42
+ gem 'pelusa', '~> 0.2.1'
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ guard :bundler do
4
+ watch('Gemfile')
5
+ end
6
+
7
+ guard :rspec, :version => 2 do
8
+ # run all specs if the spec_helper or supporting files files are modified
9
+ watch('spec/spec_helper.rb') { 'spec' }
10
+ watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec' }
11
+
12
+ # run unit specs if associated lib code is modified
13
+ watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}"] }
14
+ watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec' }
15
+
16
+ # run a spec if it is modified
17
+ watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
18
+ end
@@ -0,0 +1,56 @@
1
+ anima
2
+ =====
3
+
4
+ [![Build Status](https://secure.travis-ci.org/mbj/anima.png?branch=master)](http://travis-ci.org/mbj/anima)
5
+ [![Dependency Status](https://gemnasium.com/mbj/anima.png)](https://gemnasium.com/mbj/anima)
6
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/mbj/anima)
7
+
8
+ Simple library to declare read only attributes on objects that are initializable via attributes hash.
9
+
10
+ Installation
11
+ ------------
12
+
13
+ There is no gem release and the code is under development and unusable.
14
+
15
+ Examples
16
+ --------
17
+
18
+ See specs.
19
+
20
+ Credits
21
+ -------
22
+
23
+ Contributing
24
+ -------------
25
+
26
+ * Fork the project.
27
+ * Make your feature addition or bug fix.
28
+ * Add tests for it. This is important so I don't break it in a
29
+ future version unintentionally.
30
+ * Commit, do not mess with Rakefile or version
31
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
32
+ * Send me a pull request. Bonus points for topic branches.
33
+
34
+ License
35
+ -------
36
+
37
+ Copyright (c) 2012 Markus Schirp
38
+
39
+ Permission is hereby granted, free of charge, to any person obtaining
40
+ a copy of this software and associated documentation files (the
41
+ "Software"), to deal in the Software without restriction, including
42
+ without limitation the rights to use, copy, modify, merge, publish,
43
+ distribute, sublicense, and/or sell copies of the Software, and to
44
+ permit persons to whom the Software is furnished to do so, subject to
45
+ the following conditions:
46
+
47
+ The above copyright notice and this permission notice shall be
48
+ included in all copies or substantial portions of the Software.
49
+
50
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
51
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
52
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
53
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
54
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
55
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
56
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ require 'devtools'
2
+
3
+ Devtools.init
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * Use module subclass trick to improve expressiveness of Attribute / AttributeSet / ClassMethod
2
+ * Add ability to freeze attribute_set via dsl
3
+ * Add full unit spec coverage
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'anima'
5
+ s.version = '0.0.2'
6
+
7
+ s.authors = ['Markus Schirp']
8
+ s.email = 'mbj@seonic.net'
9
+ s.summary = 'Attributes for Plain Old Ruby Objects Experiment'
10
+ s.homepage = 'http://github.com/mbj/anima'
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
14
+ s.require_paths = %w(lib)
15
+ s.extra_rdoc_files = %w(README.md)
16
+
17
+ s.add_dependency('backports', '~> 2.6.4')
18
+ s.add_dependency('adamantium', '~> 0.0.3')
19
+ s.add_dependency('equalizer', '~> 0.0.1')
20
+ s.add_dependency('abstract_type', '~> 0.0.2')
21
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ threshold: 10
3
+ total_score: 61
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 15.1
@@ -0,0 +1,3 @@
1
+ ---
2
+ name: anima
3
+ namespace: Anima
@@ -0,0 +1,18 @@
1
+ ---
2
+ AbcMetricMethodCheck: { score: 11.0 }
3
+ AssignmentInConditionalCheck: { }
4
+ CaseMissingElseCheck: { }
5
+ ClassLineCountCheck: { line_count: 293 }
6
+ ClassNameCheck: { pattern: !ruby/regexp '/\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/' }
7
+ ClassVariableCheck: { }
8
+ CyclomaticComplexityBlockCheck: { complexity: 2 }
9
+ CyclomaticComplexityMethodCheck: { complexity: 4 }
10
+ EmptyRescueBodyCheck: { }
11
+ ForLoopCheck: { }
12
+ # TODO: decrease line_count to 5 to 10
13
+ MethodLineCountCheck: { line_count: 14 }
14
+ MethodNameCheck: { pattern: !ruby/regexp '/\A(?:[a-z\d](?:_?[a-z\d])+[?!=]?|\[\]=?|==|<=>|[+*&|-])\z/' }
15
+ ModuleLineCountCheck: { line_count: 295 }
16
+ ModuleNameCheck: { pattern: !ruby/regexp '/\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/' }
17
+ # TODO: decrease parameter_count to 2 or less
18
+ ParameterNumberCheck: { parameter_count: 3 }
@@ -0,0 +1,93 @@
1
+ ---
2
+ UncommunicativeParameterName:
3
+ accept: []
4
+ exclude: []
5
+ enabled: true
6
+ reject:
7
+ - !ruby/regexp /^.$/
8
+ - !ruby/regexp /[0-9]$/
9
+ - !ruby/regexp /[A-Z]/
10
+ LargeClass:
11
+ max_methods: 10
12
+ exclude: []
13
+ enabled: true
14
+ max_instance_variables: 2
15
+ UncommunicativeMethodName:
16
+ accept: []
17
+ exclude: []
18
+ enabled: true
19
+ reject:
20
+ - !ruby/regexp /^[a-z]$/
21
+ - !ruby/regexp /[0-9]$/
22
+ - !ruby/regexp /[A-Z]/
23
+ LongParameterList:
24
+ max_params: 2 # TODO: decrease max_params to 2
25
+ exclude: []
26
+ enabled: true
27
+ overrides: {}
28
+ FeatureEnvy:
29
+ exclude:
30
+ - Anima#attributes_hash
31
+ enabled: true
32
+ ClassVariable:
33
+ exclude: []
34
+ enabled: true
35
+ BooleanParameter:
36
+ exclude: []
37
+ enabled: true
38
+ IrresponsibleModule:
39
+ exclude: []
40
+ enabled: true
41
+ UncommunicativeModuleName:
42
+ accept: []
43
+ exclude: []
44
+ enabled: true
45
+ reject:
46
+ - !ruby/regexp /^.$/
47
+ - !ruby/regexp /[0-9]$/
48
+ NestedIterators:
49
+ ignore_iterators: []
50
+ exclude:
51
+ - Anima::Attribute#define_reader # 2 levels
52
+ enabled: true
53
+ max_allowed_nesting: 1
54
+ LongMethod:
55
+ max_statements: 7 # TODO: decrease max_statements to 5 or less
56
+ exclude: []
57
+ enabled: true
58
+ Duplication:
59
+ allow_calls: []
60
+ exclude: []
61
+ enabled: true
62
+ max_calls: 1
63
+ UtilityFunction:
64
+ max_helper_calls: 1
65
+ exclude: []
66
+ enabled: true
67
+ Attribute:
68
+ exclude: []
69
+ enabled: false
70
+ UncommunicativeVariableName:
71
+ accept: []
72
+ exclude: []
73
+ enabled: true
74
+ reject:
75
+ - !ruby/regexp /^.$/
76
+ - !ruby/regexp /[0-9]$/
77
+ - !ruby/regexp /[A-Z]/
78
+ SimulatedPolymorphism:
79
+ exclude: []
80
+ enabled: true
81
+ max_ifs: 1
82
+ DataClump:
83
+ exclude: []
84
+ enabled: true
85
+ max_copies: 1
86
+ min_clump_size: 3
87
+ ControlCouple:
88
+ exclude: []
89
+ enabled: true
90
+ LongYieldList:
91
+ max_params: 1
92
+ exclude: []
93
+ enabled: true
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 100
@@ -0,0 +1,167 @@
1
+ require 'backports'
2
+ require 'adamantium'
3
+ require 'equalizer'
4
+ require 'abstract_type'
5
+
6
+ # Main library namespace and mixin
7
+ class Anima < Module
8
+ include Adamantium::Flat
9
+
10
+ # Return names
11
+ #
12
+ # @return [AttriuteSet]
13
+ #
14
+ # @api private
15
+ #
16
+ attr_reader :attributes
17
+
18
+ # Initialize object
19
+ #
20
+ # @return [undefined]
21
+ #
22
+ # @api private
23
+ #
24
+ def initialize(*names)
25
+ @attributes = names.map { |name| Attribute.new(name) }.freeze
26
+ end
27
+
28
+ # Return attributes hash for instance
29
+ #
30
+ # @param [Object] object
31
+ #
32
+ # @return [Hash]
33
+ #
34
+ # @api private
35
+ #
36
+ def attributes_hash(object)
37
+ attributes.each_with_object({}) do |attribute, attributes_hash|
38
+ attributes_hash[attribute.name] = attribute.get(object)
39
+ end
40
+ end
41
+
42
+ # Return attribute names
43
+ #
44
+ # @return [Enumerable<Symbol>]
45
+ #
46
+ # @api private
47
+ #
48
+ def attribute_names
49
+ attributes.map(&:name)
50
+ end
51
+ memoize :attribute_names
52
+
53
+ # Initialize instance
54
+ #
55
+ # @param [Object] object
56
+ #
57
+ # @param [Hash] attribute_hash
58
+ #
59
+ # @return [self]
60
+ #
61
+ # @api private
62
+ #
63
+ def initialize_instance(object, attribute_hash)
64
+ attributes.each do |attribute|
65
+ attribute.load(object, attribute_hash)
66
+ end
67
+
68
+ overflow = attribute_hash.keys - attribute_names
69
+
70
+ unless overflow.empty?
71
+ raise Error::Unknown.new(object.class, overflow)
72
+ end
73
+
74
+ self
75
+ end
76
+
77
+ private
78
+
79
+ # Hook called when module is included
80
+ #
81
+ # @param [Class, Module] scope
82
+ #
83
+ # @return [undefined]
84
+ #
85
+ # @api private
86
+ #
87
+ def included(scope)
88
+ define_anima_method(scope)
89
+ define_initializer(scope)
90
+ define_attribute_readers(scope)
91
+ define_attribute_hash_reader(scope)
92
+ define_equalizer(scope)
93
+ end
94
+
95
+ # Define anima method on scope
96
+ #
97
+ # @param [Class, Module] scope
98
+ #
99
+ # @return [undefined]
100
+ #
101
+ # @api private
102
+ #
103
+ def define_anima_method(scope)
104
+ anima = self
105
+
106
+ scope.define_singleton_method(:anima) do
107
+ anima
108
+ end
109
+ end
110
+
111
+ # Define equalizer on scope
112
+ #
113
+ # @param [Class, Module] scope
114
+ #
115
+ # @return [undefined]
116
+ #
117
+ # @api private
118
+ #
119
+ def define_equalizer(scope)
120
+ scope.send(:include, Equalizer.new(*attribute_names))
121
+ end
122
+
123
+ # Define attribute readers
124
+ #
125
+ # @param [Class, Module] scope
126
+ #
127
+ # @return [undefined]
128
+ #
129
+ # @api private
130
+ #
131
+ def define_attribute_readers(scope)
132
+ attributes.each do |attribute|
133
+ attribute.define_reader(scope)
134
+ end
135
+ end
136
+
137
+ # Define initializer
138
+ #
139
+ # @param [Class, Module] scope
140
+ #
141
+ # @return [undefined]
142
+ #
143
+ # @api private
144
+ #
145
+ def define_initializer(scope)
146
+ scope.send(:define_method, :initialize) do |attributes|
147
+ self.class.anima.initialize_instance(self, attributes)
148
+ end
149
+ end
150
+
151
+ # Define attribute hash reader
152
+ #
153
+ # @param [Class, Module] scope
154
+ #
155
+ # @return [undefined]
156
+ #
157
+ # @api private
158
+ #
159
+ def define_attribute_hash_reader(scope)
160
+ scope.define_singleton_method(:attribute_hash) do |object|
161
+ anima.attributes_hash(object)
162
+ end
163
+ end
164
+ end
165
+
166
+ require 'anima/error'
167
+ require 'anima/attribute'
@@ -0,0 +1,99 @@
1
+ class Anima
2
+
3
+ # An attribute
4
+ class Attribute
5
+ include Adamantium::Flat
6
+
7
+ # Return attribute name
8
+ #
9
+ # @return [Symbol]
10
+ #
11
+ # @api private
12
+ #
13
+ attr_reader :name
14
+
15
+ # Load attribute
16
+ #
17
+ # @param [Object] object
18
+ # @param [Hash] attributes
19
+ #
20
+ # @return [self]
21
+ #
22
+ # @api private
23
+ #
24
+ def load(object, attributes)
25
+ attribute_name = name
26
+
27
+ value = attributes.fetch(attribute_name) do
28
+ raise Error::Missing.new(object.class, attribute_name)
29
+ end
30
+
31
+ set(object, value)
32
+ end
33
+
34
+ # Get attribute value from object
35
+ #
36
+ # @param [Object] object
37
+ #
38
+ # @return [Object]
39
+ #
40
+ # @api private
41
+ #
42
+ def get(object)
43
+ object.public_send(name)
44
+ end
45
+
46
+ # Set attribute value in object
47
+ #
48
+ # @param [Object] object
49
+ # @param [Object] value
50
+ #
51
+ # @return [self]
52
+ #
53
+ # @api private
54
+ #
55
+ def set(object, value)
56
+ object.instance_variable_set(instance_variable_name, value)
57
+
58
+ self
59
+ end
60
+
61
+ # Return instance variable name
62
+ #
63
+ # @return [Symbol]
64
+ # returns @ prefixed name
65
+ #
66
+ # @api private
67
+ #
68
+ def instance_variable_name
69
+ :"@#{name}"
70
+ end
71
+ memoize :instance_variable_name
72
+
73
+ # Define reader
74
+ #
75
+ # @param [Class, Module] scope
76
+ #
77
+ # @return [self]
78
+ #
79
+ # @api private
80
+ #
81
+ def define_reader(scope)
82
+ scope.send(:attr_reader, name)
83
+ self
84
+ end
85
+
86
+ private
87
+
88
+
89
+ # Initialize attribute
90
+ #
91
+ # @param [Symbol] name
92
+ #
93
+ # @api private
94
+ #
95
+ def initialize(name)
96
+ @name = name
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,29 @@
1
+ class Anima
2
+
3
+ # Abstract base class for anima errors
4
+ class Error < RuntimeError
5
+ include AbstractType
6
+
7
+ # Initialize object
8
+ #
9
+ # @param [Class] model
10
+ # @param [Enumerable<Symbol>] names
11
+ #
12
+ # @return [undefined]
13
+ #
14
+ # @api private
15
+ #
16
+ def initialize(model, names)
17
+ super("#{self.class.name.split('::').last} attribute(s) #{names.inspect} for #{model.name}")
18
+ end
19
+
20
+ # Error for unknown attributes
21
+ class Unknown < self
22
+ end
23
+
24
+ # Error for missing attributes
25
+ class Missing < self
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima, 'simple integration' do
4
+ subject { class_under_test.new(attributes) }
5
+
6
+ let(:class_under_test) do
7
+ Class.new do
8
+ include Anima.new(:firstname, :lastname)
9
+
10
+ def self.name
11
+ 'TestClass'
12
+ end
13
+ end
14
+ end
15
+
16
+ context 'when instanciated with all attributes' do
17
+ let(:attributes) do
18
+ {
19
+ :firstname => 'Markus',
20
+ :lastname => 'Schirp'
21
+ }
22
+ end
23
+
24
+ its(:firstname) { should eql('Markus') }
25
+ its(:lastname) { should eql('Schirp') }
26
+ end
27
+
28
+ context 'with instanciated with extra attributes' do
29
+ let(:attributes) do
30
+ {
31
+ :firstname => 'Markus',
32
+ :lastname => 'Schirp',
33
+ :extra => 'Foo'
34
+ }
35
+ end
36
+
37
+ it 'should raise error' do
38
+ expect { subject }.to raise_error(
39
+ Anima::Error::Unknown,
40
+ 'Unknown attribute(s) [:extra] for TestClass'
41
+ )
42
+ end
43
+ end
44
+
45
+ context 'when instanciated with missing attribute' do
46
+
47
+ let(:attributes) { {} }
48
+
49
+ it 'should raise error' do
50
+ expect { subject }.to raise_error(
51
+ Anima::Error::Missing,
52
+ 'Missing attribute(s) :firstname for TestClass'
53
+ )
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a command method' do
4
+ it 'returns self' do
5
+ should equal(object)
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an #each method' do
4
+ it_should_behave_like 'a command method'
5
+
6
+ context 'with no block' do
7
+ subject { object.each }
8
+
9
+ it { should be_instance_of(to_enum.class) }
10
+
11
+ it 'yields the expected values' do
12
+ subject.to_a.should eql(object.to_a)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a hash method' do
4
+ it_should_behave_like 'an idempotent method'
5
+
6
+ specification = proc do
7
+ should be_instance_of(Fixnum)
8
+ end
9
+
10
+ it 'is a fixnum' do
11
+ instance_eval(&specification)
12
+ end
13
+
14
+ it 'memoizes the hash code' do
15
+ subject.should eql(object.memoized(:hash))
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an idempotent method' do
4
+ it 'is idempotent' do
5
+ should equal(instance_eval(&self.class.subject))
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an invertible method' do
4
+ it_should_behave_like 'an idempotent method'
5
+
6
+ it 'is invertible' do
7
+ subject.inverse.should equal(object)
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'anima'
4
+ require 'rspec'
5
+
6
+ # require spec support files and shared behavior
7
+ Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each { |f| require(f) }
8
+
9
+
10
+ RSpec.configure do |config|
11
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima::Attribute, '#define_reader' do
4
+ let(:object) { described_class.new(:foo) }
5
+
6
+ subject { object.define_reader(target_class) }
7
+
8
+ let(:target_class) do
9
+ Class.new do
10
+ def initialize(foo)
11
+ @foo = foo
12
+ end
13
+ end
14
+ end
15
+
16
+ let(:value) { mock('Value') }
17
+
18
+ it 'should create a reader' do
19
+ instance = target_class.new(value)
20
+ lambda { subject }.should change { instance.respond_to?(:foo) }.from(false).to(true)
21
+ end
22
+
23
+ it_should_behave_like 'a command method'
24
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima::Attribute, '#get' do
4
+ let(:object) { described_class.new(:foo) }
5
+
6
+ subject { object.get(target) }
7
+
8
+ let(:target_class) do
9
+ Class.new do
10
+ attr_reader :foo
11
+
12
+ def initialize(foo)
13
+ @foo = foo
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:target) { target_class.new(value) }
19
+
20
+ let(:value) { mock('Value') }
21
+
22
+ it 'should return value' do
23
+ should be(value)
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima::Attribute, '#instance_variable_name' do
4
+ subject { Anima::Attribute.new(:foo).instance_variable_name }
5
+
6
+ it { should be(:@foo) }
7
+
8
+ it_should_behave_like 'an idempotent method'
9
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima::Attribute, '#load' do
4
+ let(:object) { described_class.new(:foo) }
5
+
6
+ subject { object.load(target, attribute_hash) }
7
+
8
+ let(:target) { Object.new }
9
+
10
+ let(:value) { mock('Value') }
11
+
12
+ context 'when attribute hash contains key' do
13
+ let(:attribute_hash) { { :foo => value } }
14
+
15
+ it 'should set value as instance variable' do
16
+ subject
17
+ target.instance_variable_get(:@foo).should be(value)
18
+ end
19
+
20
+ it_should_behave_like 'a command method'
21
+
22
+ end
23
+
24
+ context 'when attribute hash does not contain key' do
25
+ let(:attribute_hash) { {} }
26
+
27
+ it 'should raise error' do
28
+ expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target.class, :foo).message)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima::Attribute, '#set' do
4
+ let(:object) { described_class.new(:foo) }
5
+
6
+ subject { object.set(target, value) }
7
+
8
+ let(:target) { Object.new }
9
+
10
+ let(:value) { mock('Value') }
11
+
12
+ it_should_behave_like 'a command method'
13
+
14
+ it 'should set value as instance variable' do
15
+ subject
16
+ target.instance_variable_get(:@foo).should be(value)
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima, '#attributes_hash' do
4
+ let(:object) { described_class.new(:foo) }
5
+
6
+ let(:value) { mock('Value') }
7
+
8
+ let(:instance) { mock(:foo => value) }
9
+
10
+ subject { object.attributes_hash(instance) }
11
+
12
+ it { should eql(:foo => value) }
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima::Error, '#message' do
4
+ let(:object) { described_class.new(klass, name) }
5
+
6
+ subject { object.message }
7
+
8
+ let(:klass) { mock(:name => 'THE-CLASS-NAME') }
9
+ let(:name) { 'THE-ATTRIBUTE-NAME' }
10
+
11
+ it 'should return the message string' do
12
+ should eql('Error attribute(s) "THE-ATTRIBUTE-NAME" for THE-CLASS-NAME')
13
+ end
14
+
15
+ it_should_behave_like 'an idempotent method'
16
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima, '#included' do
4
+ let(:object) { described_class.new(:foo) }
5
+
6
+ let(:target) do
7
+ object = self.object
8
+ Class.new do
9
+ include object
10
+ end
11
+ end
12
+
13
+ let(:value) { mock('Value') }
14
+
15
+ let(:instance) { target.new(:foo => value) }
16
+ let(:instance_b) { target.new(:foo => value) }
17
+ let(:instance_c) { target.new(:foo => mock('Bar')) }
18
+
19
+ context 'on instance' do
20
+ subject { instance }
21
+
22
+ its(:foo) { should be(value) }
23
+
24
+ it { should eql(instance_b) }
25
+ end
26
+
27
+ context 'on singleton' do
28
+ subject { target }
29
+
30
+ it 'should define attribute hash reader' do
31
+ target.attribute_hash(instance).should eql(:foo => value)
32
+ end
33
+
34
+ its(:anima) { should be(object) }
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Anima, '#initialize_instance' do
4
+ let(:object) { Anima.new(:foo, :bar) }
5
+
6
+ let(:target) { Object.new }
7
+
8
+ let(:foo) { mock('Foo') }
9
+ let(:bar) { mock('Bar') }
10
+
11
+ subject { object.initialize_instance(target, attribute_hash) }
12
+
13
+ context 'when all keys are present in attribute hash' do
14
+ let(:attribute_hash) { { :foo => foo, :bar => bar } }
15
+
16
+ it 'should initialize target instance variables' do
17
+ subject
18
+ target.instance_variables.map(&:to_sym).to_set.should eql([:@foo, :@bar].to_set)
19
+ target.instance_variable_get(:@foo).should be(foo)
20
+ target.instance_variable_get(:@bar).should be(bar)
21
+ end
22
+
23
+ it_should_behave_like 'a command method'
24
+ end
25
+
26
+ context 'when extra key is missing in attribute hash' do
27
+ let(:attribute_hash) { { :foo => foo, :bar => bar, :baz => mock('Baz') } }
28
+
29
+ it 'should raise error' do
30
+ expect { subject }.to raise_error(Anima::Error::Unknown, Anima::Error::Unknown.new(target.class, [:baz]).message)
31
+ end
32
+ end
33
+
34
+ context 'when a key is missing in attribute hash' do
35
+ let(:attribute_hash) { { :bar => bar } }
36
+
37
+ it 'should raise error' do
38
+ expect { subject }.to raise_error(Anima::Error::Missing, Anima::Error::Missing.new(target.class, :foo).message)
39
+ end
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anima
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Markus Schirp
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: backports
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.6.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.6.4
30
+ - !ruby/object:Gem::Dependency
31
+ name: adamantium
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.0.3
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.0.3
46
+ - !ruby/object:Gem::Dependency
47
+ name: equalizer
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.0.1
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: abstract_type
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.0.2
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.0.2
78
+ description:
79
+ email: mbj@seonic.net
80
+ executables: []
81
+ extensions: []
82
+ extra_rdoc_files:
83
+ - README.md
84
+ files:
85
+ - .gitignore
86
+ - .rspec
87
+ - .travis.yml
88
+ - Changelog.md
89
+ - Gemfile
90
+ - Gemfile.devtools
91
+ - Guardfile
92
+ - README.md
93
+ - Rakefile
94
+ - TODO
95
+ - anima.gemspec
96
+ - config/flay.yml
97
+ - config/flog.yml
98
+ - config/mutant.yml
99
+ - config/roodi.yml
100
+ - config/site.reek
101
+ - config/yardstick.yml
102
+ - lib/anima.rb
103
+ - lib/anima/attribute.rb
104
+ - lib/anima/error.rb
105
+ - spec/integration/simple_spec.rb
106
+ - spec/shared/command_method_behavior.rb
107
+ - spec/shared/each_method_behaviour.rb
108
+ - spec/shared/hash_method_behavior.rb
109
+ - spec/shared/idempotent_method_behavior.rb
110
+ - spec/shared/invertible_method_behaviour.rb
111
+ - spec/spec_helper.rb
112
+ - spec/unit/anima/attribute/define_reader_spec.rb
113
+ - spec/unit/anima/attribute/get_spec.rb
114
+ - spec/unit/anima/attribute/instance_variable_name_spec.rb
115
+ - spec/unit/anima/attribute/load_spec.rb
116
+ - spec/unit/anima/attribute/set_spec.rb
117
+ - spec/unit/anima/attributes_hash_spec.rb
118
+ - spec/unit/anima/error/message_spec.rb
119
+ - spec/unit/anima/included_spec.rb
120
+ - spec/unit/anima/initialize_instance_spec.rb
121
+ homepage: http://github.com/mbj/anima
122
+ licenses: []
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 1.8.24
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: Attributes for Plain Old Ruby Objects Experiment
145
+ test_files:
146
+ - spec/integration/simple_spec.rb
147
+ - spec/shared/command_method_behavior.rb
148
+ - spec/shared/each_method_behaviour.rb
149
+ - spec/shared/hash_method_behavior.rb
150
+ - spec/shared/idempotent_method_behavior.rb
151
+ - spec/shared/invertible_method_behaviour.rb
152
+ - spec/spec_helper.rb
153
+ - spec/unit/anima/attribute/define_reader_spec.rb
154
+ - spec/unit/anima/attribute/get_spec.rb
155
+ - spec/unit/anima/attribute/instance_variable_name_spec.rb
156
+ - spec/unit/anima/attribute/load_spec.rb
157
+ - spec/unit/anima/attribute/set_spec.rb
158
+ - spec/unit/anima/attributes_hash_spec.rb
159
+ - spec/unit/anima/error/message_spec.rb
160
+ - spec/unit/anima/included_spec.rb
161
+ - spec/unit/anima/initialize_instance_spec.rb
162
+ has_rdoc: