adamantium 0.0.2 → 0.0.3

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.
@@ -1,5 +1,5 @@
1
1
  language: ruby
2
- bundler_args: --without yard guard metrics
2
+ bundler_args: --without yard guard
3
3
  script: "bundle exec rake spec"
4
4
  rvm:
5
5
  - 1.8.7
data/Gemfile CHANGED
@@ -21,12 +21,17 @@ platform :jruby do
21
21
  end
22
22
 
23
23
  group :metrics do
24
- gem 'flay', '~> 1.4.2'
25
- gem 'flog', '~> 2.5.1'
26
- gem 'reek', '~> 1.2.8', :github => 'dkubb/reek'
27
- gem 'roodi', '~> 2.1.0'
28
- gem 'yardstick', '~> 0.7.0'
29
- gem 'yard-spellcheck', '~> 0.1.5'
24
+ gem 'flay', '~> 1.4.2'
25
+ gem 'flog', '~> 2.5.1'
26
+ gem 'reek', '~> 1.2.8', :github => 'dkubb/reek'
27
+ gem 'roodi', '~> 2.1.0'
28
+ gem 'yardstick', '~> 0.7.0'
29
+ gem 'simplecov'
30
+
31
+ platforms :ruby_18, :ruby_19 do
32
+ # this indirectly depends on ffi which does not build on ruby-head
33
+ gem 'yard-spellcheck', '~> 0.1.5'
34
+ end
30
35
 
31
36
  platforms :mri_18 do
32
37
  gem 'arrayfields', '~> 4.7.4' # for metric_fu
data/README.md CHANGED
@@ -26,24 +26,95 @@ Examples
26
26
  require 'adamantium'
27
27
  require 'securerandom'
28
28
 
29
- class Foo
29
+ class Example
30
+ # Inclusion of Adamantium defaults to deep freeze behavior
31
+ # of constructor and memoizer
32
+
30
33
  include Adamantium
31
34
 
32
- def bar
33
- SecureRandom.hex(6)
35
+ # Instance and attributes (ivars) are frozen per default
36
+ # Example:
37
+ #
38
+ # object = Example.new
39
+ # object.frozen? # => true
40
+ # object.attribute.frozen? # => true
41
+ #
42
+ def initialize
43
+ @attribute = "foo bar"
34
44
  end
35
-
36
- memoize :bar
45
+ attr_reader :attribute
46
+
47
+ # Memoized method with deeply frozen value (default)
48
+ # Example:
49
+ #
50
+ # object = Example.new
51
+ # object.random => ["abcdef"]
52
+ # object.random => ["abcdef"]
53
+ # object.random.frozen? => true
54
+ # object.random[0].frozen? => true
55
+ #
56
+ def random
57
+ [SecureRandom.hex(6)]
58
+ end
59
+ memoize :random
60
+
61
+ # Memoized method with non frozen value
62
+ # Example:
63
+ #
64
+ # object = Example.new
65
+ # object.buffer # => <StringIO:abcdef>
66
+ # object.buffer # => <StringIO:abcdef>
67
+ # object.buffer.frozen? # => false
68
+ #
69
+ def buffer
70
+ StringIO.new
71
+ end
72
+ memoize :buffer, :freezer => :noop
73
+
74
+ # Memoized method with nondeeply frozen value
75
+ # Example:
76
+ #
77
+ # object = Example.new
78
+ # object.random2 => ["abcdef"]
79
+ # object.random2 => ["abcdef"]
80
+ # object.random2.frozen? => true
81
+ # object.random2[0].frozen? => false
82
+ #
83
+ def random2
84
+ [SecureRandom.hex(6)]
85
+ end
86
+ memoize :random2, :freezer => :flat
37
87
  end
38
88
 
39
- object = Foo.new
40
- object.bar # => "abcdef"
41
- # Value is memoized
42
- object.bar # => "abcdef"
43
- # Returns the same object on all calls
44
- object.bar.equal?(object.bar) # => true
45
- # Object is frozen
46
- object.frozen? # => true
89
+ class FlatExample
90
+ # Inclusion of Adamantium::Flat defaults do non deep frozen behavior
91
+ # for memoizer and constructor
92
+
93
+ include Adamantium::Flat
94
+
95
+ # Instace is frozen but attribute is not
96
+ # object = FlatExample.new
97
+ # object.frozen? # => true
98
+ # object.attribute.frozen? # => false
99
+ def initialize
100
+ @attribute = "foo bar"
101
+ end
102
+ attr_reader :attribute
103
+
104
+ # Memoized method with flat frozen value (default)
105
+ # Example:
106
+ #
107
+ # object = Example.new
108
+ # object.random => ["abcdef"]
109
+ # object.random => ["abcdef"]
110
+ # object.random.frozen? => true
111
+ # object.random[0].frozen? => false
112
+ #
113
+ def random
114
+ [SecureRandom.hex(6)]
115
+ end
116
+ memoize :random
117
+ end
47
118
  ```
48
119
 
49
120
  Credits
@@ -68,7 +139,7 @@ License
68
139
  -------
69
140
 
70
141
  Copyright (c) 2009-2012 Dan Kubb
71
- Copyright (c) 2012 Markus Schirp (packaging)
142
+ Copyright (c) 2012 Markus Schirp
72
143
 
73
144
  Permission is hereby granted, free of charge, to any person obtaining
74
145
  a copy of this software and associated documentation files (the
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 9
3
- total_score: 29
3
+ total_score: 61
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 23.4
2
+ threshold: 21.1
@@ -46,7 +46,8 @@ UncommunicativeModuleName:
46
46
  - !ruby/regexp /[0-9]$/
47
47
  NestedIterators:
48
48
  ignore_iterators: []
49
- exclude: []
49
+ exclude:
50
+ - Adamantium::ModuleMethods#define_memoize_method # 2 levels
50
51
  enabled: true
51
52
  max_allowed_nesting: 1
52
53
  LongMethod:
@@ -6,6 +6,35 @@ module Adamantium
6
6
  # Storage for memoized methods
7
7
  Memory = Class.new(::Hash)
8
8
 
9
+ # Defaults to less strict defaults
10
+ module Flat
11
+
12
+ # Return flat freezer
13
+ #
14
+ # @return [Freezer::Flat]
15
+ #
16
+ # @api private
17
+ #
18
+ def freezer
19
+ Freezer::Flat
20
+ end
21
+
22
+ # Hook called when module is included
23
+ #
24
+ # @param [Class,Module] descendant
25
+ #
26
+ # @return [self]
27
+ #
28
+ # @api private
29
+ def self.included(descendant)
30
+ super
31
+ descendant.send(:include, Adamantium)
32
+ descendant.extend(self)
33
+
34
+ self
35
+ end
36
+ end
37
+
9
38
  # Hook called when module is included
10
39
  #
11
40
  # @param [Module] descendant
@@ -15,51 +44,11 @@ module Adamantium
15
44
  #
16
45
  # @api private
17
46
  def self.included(descendant)
18
- super
19
47
  descendant.extend ModuleMethods if descendant.kind_of?(Module)
20
48
  descendant.extend ClassMethods if descendant.kind_of?(Class)
21
49
  self
22
50
  end
23
51
 
24
- # Attempt to freeze an object
25
- #
26
- # @example using a value object
27
- # Adamantium.freeze_object(12345) # => noop
28
- #
29
- # @example using a normal object
30
- # Adamantium.freeze_object({}) # => duplicate & freeze object
31
- #
32
- # @param [Object] object
33
- # the object to freeze
34
- #
35
- # @return [Object]
36
- # if supported, the frozen object, otherwise the object directly
37
- #
38
- # @api public
39
- def self.freeze_object(object)
40
- case object
41
- when Numeric, TrueClass, FalseClass, NilClass, Symbol
42
- object
43
- else
44
- freeze_value(object)
45
- end
46
- end
47
-
48
- # Returns a frozen value
49
- #
50
- # @param [Object] value
51
- # a value to freeze
52
- #
53
- # @return [Object]
54
- # if frozen, the value directly, otherwise a frozen copy of the value
55
- #
56
- # @api private
57
- def self.freeze_value(value)
58
- value.frozen? ? value : IceNine.deep_freeze(value.dup)
59
- end
60
-
61
- private_class_method :freeze_value
62
-
63
52
  # Freeze the object
64
53
  #
65
54
  # @example
@@ -102,7 +91,10 @@ module Adamantium
102
91
  #
103
92
  # @api public
104
93
  def memoize(name, value)
105
- store_memory(name, value) unless memory.key?(name)
94
+ unless memory.key?(name)
95
+ store_memory(name, freeze_object(value))
96
+ end
97
+
106
98
  self
107
99
  end
108
100
 
@@ -129,6 +121,27 @@ private
129
121
  @__memory ||= Memory.new
130
122
  end
131
123
 
124
+ # Freeze object
125
+ #
126
+ # @param [Object] object
127
+ # an object to be frozen
128
+ #
129
+ # @return [Object]
130
+ #
131
+ # @api private
132
+ def freeze_object(object)
133
+ freezer.call(object)
134
+ end
135
+
136
+ # Return class level freezer
137
+ #
138
+ # @return [#call]
139
+ #
140
+ # @api private
141
+ def freezer
142
+ self.class.freezer
143
+ end
144
+
132
145
  # Store the value in memory
133
146
  #
134
147
  # @param [Symbol] name
@@ -142,108 +155,10 @@ private
142
155
  #
143
156
  # @api private
144
157
  def store_memory(name, value)
145
- memory[name] = Adamantium.freeze_object(value)
158
+ memory[name] = value
146
159
  end
147
-
148
- # Methods mixed in to adamantium modules
149
- module ModuleMethods
150
-
151
- # Hook called when module is included
152
- #
153
- # @param [Module] mod
154
- # the module including ModuleMethods
155
- #
156
- # @return [self]
157
- #
158
- # @api private
159
- def included(mod)
160
- Adamantium.included(mod)
161
- self
162
- end
163
-
164
- # Memoize a list of methods
165
- #
166
- # @example
167
- # memoize :hash
168
- #
169
- # @param [Array<#to_s>] methods
170
- # a list of methods to memoize
171
- #
172
- # @return [self]
173
- #
174
- # @api public
175
- def memoize(*methods)
176
- methods.each { |method| memoize_method(method) }
177
- self
178
- end
179
-
180
- private
181
-
182
- # Memoize the named method
183
- #
184
- # @param [#to_s] method
185
- # a method to memoize
186
- #
187
- # @return [undefined]
188
- #
189
- # @api private
190
- def memoize_method(method)
191
- visibility = method_visibility(method)
192
- define_memoize_method(method)
193
- send(visibility, method)
194
- end
195
-
196
- # Define a memoized method that delegates to the original method
197
- #
198
- # @param [Symbol] method
199
- # the name of the method
200
- #
201
- # @return [undefined]
202
- #
203
- # @api private
204
- def define_memoize_method(method)
205
- original = instance_method(method)
206
- undef_method(method)
207
- define_method(method) do |*args|
208
- if memory.key?(method)
209
- memoized(method)
210
- else
211
- store_memory(method, original.bind(self).call(*args))
212
- end
213
- end
214
- end
215
-
216
- # Return the method visibility of a method
217
- #
218
- # @param [String, Symbol] method
219
- # the name of the method
220
- #
221
- # @return [String]
222
- #
223
- # @api private
224
- def method_visibility(method)
225
- if private_method_defined?(method) then 'private'
226
- elsif protected_method_defined?(method) then 'protected'
227
- else 'public'
228
- end
229
- end
230
-
231
- end # module ModuleMethods
232
-
233
- # Methods mixed in to adamantium classes
234
- module ClassMethods
235
-
236
- # Instantiate a new frozen object
237
- #
238
- # @example
239
- # object = AdamantiumClass.new # object is frozen
240
- #
241
- # @return [Object]
242
- #
243
- # @api public
244
- def new(*)
245
- IceNine.deep_freeze(super)
246
- end
247
-
248
- end # module ClassMethods
249
160
  end # module Adamantium
161
+
162
+ require 'adamantium/module_methods'
163
+ require 'adamantium/class_methods'
164
+ require 'adamantium/freezer'
@@ -0,0 +1,20 @@
1
+ module Adamantium
2
+
3
+ # Methods mixed in to adamantium classes
4
+ module ClassMethods
5
+
6
+ # Instantiate a new frozen object
7
+ #
8
+ # @example
9
+ # object = AdamantiumClass.new # object is frozen
10
+ #
11
+ # @return [Object]
12
+ #
13
+ # @api public
14
+ def new(*)
15
+ freezer.freeze(super)
16
+ end
17
+
18
+ end # module ClassMethods
19
+
20
+ end
@@ -0,0 +1,139 @@
1
+ module Adamantium
2
+
3
+ # Abstract base class for freezers
4
+ #
5
+ # TODO: Use dkubb/abstract_class?
6
+ #
7
+ # Better pattern for singleton inheritance/shared code
8
+ class Freezer
9
+
10
+ private_class_method :new
11
+
12
+ # Attempt to freeze an object
13
+ #
14
+ # @example using a value object
15
+ # Adamantium.freeze_object(12345) # => noop
16
+ #
17
+ # @example using a normal object
18
+ # Adamantium.freeze_object({}) # => duplicate & freeze object
19
+ #
20
+ # @param [Object] object
21
+ # the object to freeze
22
+ #
23
+ # @return [Object]
24
+ # if supported, the frozen object, otherwise the object directly
25
+ #
26
+ # @api public
27
+ def self.call(object)
28
+ case object
29
+ when Numeric, TrueClass, FalseClass, NilClass, Symbol, Class, Module
30
+ object
31
+ else
32
+ freeze_value(object)
33
+ end
34
+ end
35
+
36
+ private_class_method :call
37
+
38
+ # Returns a frozen value
39
+ #
40
+ # @param [Object] value
41
+ # a value to freeze
42
+ #
43
+ # @return [Object]
44
+ # if frozen, the value directly, otherwise a frozen copy of the value
45
+ #
46
+ # @api private
47
+ def self.freeze_value(value)
48
+ value.frozen? ? value : freeze(value.dup)
49
+ end
50
+
51
+ private_class_method :freeze_value
52
+
53
+ # Freezer that does not deep freeze
54
+ class Flat < self
55
+
56
+ # Freeze value
57
+ #
58
+ # @param [Object] value
59
+ #
60
+ # @return [undefined]
61
+ #
62
+ # @api private
63
+ def self.freeze(value)
64
+ value.freeze
65
+ end
66
+ public_class_method :call
67
+ end
68
+
69
+ # Freezer that does deep freeze
70
+ class Deep < self
71
+
72
+ # Deep freeze value
73
+ #
74
+ # @param [Object] value
75
+ #
76
+ # @return [undefined]
77
+ #
78
+ # @api private
79
+ def self.freeze(value)
80
+ IceNine.deep_freeze(value)
81
+ end
82
+ public_class_method :call
83
+ end
84
+
85
+ Noop = lambda { |object| object }.freeze
86
+
87
+ # Error raised when freezer cannot be found
88
+ class UnknownFreezerError < RuntimeError; end
89
+
90
+ # Error raised when memoizer options contain unknown keys
91
+ class OptionError < RuntimeError; end
92
+
93
+ # Return freezer for name
94
+ #
95
+ # @param [Symbol] name
96
+ # a freezer name
97
+ #
98
+ # @return [#call]
99
+ #
100
+ # @api private
101
+ def self.get(name)
102
+ case name
103
+ when :noop
104
+ Noop
105
+ when :deep
106
+ Deep
107
+ when :flat
108
+ Flat
109
+ else
110
+ raise UnknownFreezerError, "Freezer with name #{name.inspect} is unknown"
111
+ end
112
+ end
113
+
114
+ # Parse freezer options
115
+ #
116
+ # @param [Hash] options
117
+ # an options hash
118
+ #
119
+ # @return [#call]
120
+ # if freezer option was present
121
+ #
122
+ # @return [nil]
123
+ # otherwise
124
+ #
125
+ # @api private
126
+ #
127
+ def self.parse(options)
128
+ keys = options.keys - [:freezer]
129
+
130
+ unless keys.empty?
131
+ raise OptionError, "Unknown option key(s) for memoizer #{keys.inspect}"
132
+ end
133
+
134
+ return unless options.key?(:freezer)
135
+
136
+ get(options.fetch(:freezer))
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,103 @@
1
+ module Adamantium
2
+
3
+ # Methods mixed in to adamantium modules
4
+ module ModuleMethods
5
+
6
+ # Hook called when module is included
7
+ #
8
+ # @param [Module] mod
9
+ # the module including ModuleMethods
10
+ #
11
+ # @return [self]
12
+ #
13
+ # @api private
14
+ def included(mod)
15
+ Adamantium.included(mod)
16
+ self
17
+ end
18
+
19
+ # Return default deep freezer
20
+ #
21
+ # @return [Freezer::Deep]
22
+ #
23
+ # @api private
24
+ #
25
+ def freezer
26
+ Freezer::Deep
27
+ end
28
+
29
+ # Memoize a list of methods
30
+ #
31
+ # @example
32
+ # memoize :hash
33
+ #
34
+ # @param [Array<#to_s>] methods
35
+ # a list of methods to memoize
36
+ #
37
+ # @return [self]
38
+ #
39
+ # @api public
40
+ def memoize(*methods)
41
+ options = methods.last.kind_of?(Hash) ? methods.pop : {}
42
+ method_freezer = Freezer.parse(options) || freezer
43
+ methods.each { |method| memoize_method(method, method_freezer) }
44
+ self
45
+ end
46
+
47
+ private
48
+
49
+ # Memoize the named method
50
+ #
51
+ # @param [#to_s] method
52
+ # a method to memoize
53
+ # @param [#call] freezer
54
+ # a freezer for memoized values
55
+ #
56
+ # @return [undefined]
57
+ #
58
+ # @api private
59
+ def memoize_method(method, freezer)
60
+ visibility = method_visibility(method)
61
+ define_memoize_method(method, freezer)
62
+ send(visibility, method)
63
+ end
64
+
65
+ # Define a memoized method that delegates to the original method
66
+ #
67
+ # @param [Symbol] method
68
+ # the name of the method
69
+ # @param [#call] freezer
70
+ # a freezer for memoized values
71
+ #
72
+ # @return [undefined]
73
+ #
74
+ # @api private
75
+ def define_memoize_method(method, freezer)
76
+ original = instance_method(method)
77
+ undef_method(method)
78
+ define_method(method) do |*args|
79
+ memory.fetch(method) do
80
+ value = original.bind(self).call(*args)
81
+ frozen = freezer.call(value)
82
+ store_memory(method, frozen)
83
+ end
84
+ end
85
+ end
86
+
87
+ # Return the method visibility of a method
88
+ #
89
+ # @param [String, Symbol] method
90
+ # the name of the method
91
+ #
92
+ # @return [Symbol]
93
+ #
94
+ # @api private
95
+ def method_visibility(method)
96
+ if private_method_defined?(method) then :private
97
+ elsif protected_method_defined?(method) then :protected
98
+ else :public
99
+ end
100
+ end
101
+
102
+ end # module ModuleMethods
103
+ end # module Adamantium
@@ -1,3 +1,3 @@
1
1
  module Adamantium
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.0.3'.freeze
3
3
  end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Adamantium do
4
+ let(:class_under_test) do
5
+ mixin = self.mixin
6
+
7
+ Class.new do
8
+ include mixin
9
+
10
+ def initialize
11
+ @attribute = Object.new
12
+ end
13
+ attr_reader :attribute
14
+
15
+ def memoized
16
+ [Object.new]
17
+ end
18
+ memoize :memoized
19
+ end
20
+ end
21
+
22
+ context 'inherited' do
23
+ let(:mixin) { Adamantium::Flat }
24
+
25
+ subject { Class.new(class_under_test).new }
26
+
27
+ it 'should return memoized value' do
28
+ subject.memoized
29
+ end
30
+ end
31
+
32
+ context 'default' do
33
+ let(:mixin) { Adamantium }
34
+
35
+ subject { class_under_test.new }
36
+
37
+ it 'should deep freeze instance and attributes' do
38
+ should be_frozen
39
+ subject.attribute.should be_frozen
40
+ end
41
+
42
+ it 'should deep freeze memoized values' do
43
+ subject.memoized.should be_frozen
44
+ subject.memoized[0].should be_frozen
45
+ end
46
+ end
47
+
48
+ context 'flat' do
49
+ let(:mixin) { Adamantium::Flat }
50
+
51
+ subject { class_under_test.new }
52
+
53
+ it 'should freeze only instance' do
54
+ should be_frozen
55
+ subject.attribute.should_not be_frozen
56
+ end
57
+
58
+ it 'should flat freeze memoized values' do
59
+ subject.memoized.should be_frozen
60
+ subject.memoized[0].should_not be_frozen
61
+ end
62
+ end
63
+ end
@@ -7,7 +7,7 @@ describe Adamantium, '#dup' do
7
7
  subject { object.dup }
8
8
 
9
9
  let(:described_class) { AdamantiumSpecs::Object }
10
- let(:object) { described_class.new }
10
+ let(:object) { described_class.new }
11
11
 
12
12
  it { should equal(object) }
13
13
  end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Adamantium::ClassMethods, '#freezer' do
4
+ let(:object) do
5
+ Class.new do
6
+ include Adamantium::Flat
7
+ end
8
+ end
9
+
10
+ subject { object.freezer }
11
+
12
+ it { should be(Adamantium::Freezer::Flat) }
13
+
14
+ it_should_behave_like 'an idempotent method'
15
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Adamantium::Freezer, '.get' do
4
+ subject { object.get(name) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ context 'with :deep' do
9
+ let(:name) { :deep }
10
+
11
+ it { should be(Adamantium::Freezer::Deep) }
12
+ end
13
+
14
+ context 'with :noop' do
15
+ let(:name) { :noop }
16
+
17
+ it { should be(Adamantium::Freezer::Noop) }
18
+ end
19
+
20
+ context 'with :flat' do
21
+ let(:name) { :flat }
22
+
23
+ it { should be(Adamantium::Freezer::Flat) }
24
+ end
25
+
26
+ context 'with unknown name' do
27
+ let(:name) { :other }
28
+
29
+ it 'should raise error' do
30
+ expect { subject }.to raise_error(described_class::UnknownFreezerError, 'Freezer with name :other is unknown')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Adamantium::Freezer, '.parse' do
4
+ subject { object.parse(options) }
5
+
6
+ let(:object) { described_class }
7
+ let(:freezer) { mock('Freezer') }
8
+
9
+ context 'with empty options' do
10
+ let(:options) { {} }
11
+ it { should be(nil) }
12
+ end
13
+
14
+ context 'with :freezer key' do
15
+ let(:options) { { :freezer => name } }
16
+ let(:name) { mock('Name') }
17
+
18
+ it 'should get freezer' do
19
+ described_class.should_receive(:get).with(name).and_return(freezer)
20
+ should be(freezer)
21
+ end
22
+ end
23
+
24
+ context 'with any other key' do
25
+ let(:options) { { :other => :key } }
26
+
27
+ it 'should raise error' do
28
+ expect { subject }.to raise_error(described_class::OptionError, 'Unknown option key(s) for memoizer [:other]')
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Adamantium::Freezer::Deep, '.call' do
6
+ subject { object.call(value) }
7
+
8
+ let(:object) { self.class.described_type }
9
+
10
+ context 'with a numeric value' do
11
+ let(:value) { 1 }
12
+
13
+ it { should equal(value) }
14
+ end
15
+
16
+ context 'with a true value' do
17
+ let(:value) { true }
18
+
19
+ it { should equal(value) }
20
+ end
21
+
22
+ context 'with a false value' do
23
+ let(:value) { false }
24
+
25
+ it { should equal(value) }
26
+ end
27
+
28
+ context 'with a nil value' do
29
+ let(:value) { nil }
30
+
31
+ it { should equal(value) }
32
+ end
33
+
34
+ context 'with a symbol value' do
35
+ let(:value) { :symbol }
36
+
37
+ it { should equal(value) }
38
+ end
39
+
40
+ context 'with a frozen value' do
41
+ let(:value) { String.new.freeze }
42
+
43
+ it { should equal(value) }
44
+ end
45
+
46
+ context 'with a module value' do
47
+ let(:value) { Module.new }
48
+
49
+ it { should equal(value) }
50
+
51
+ it { should_not be_frozen }
52
+ end
53
+
54
+ context 'with a class value' do
55
+ let(:value) { Class.new }
56
+
57
+ it { should equal(value) }
58
+
59
+ it { should_not be_frozen }
60
+ end
61
+
62
+ context 'with an unfrozen value' do
63
+ let(:value) { String.new }
64
+
65
+ it { should_not equal(value) }
66
+
67
+ it { should be_instance_of(String) }
68
+
69
+ it { should == value }
70
+
71
+ it { should be_frozen }
72
+ end
73
+
74
+ context 'with a composed value' do
75
+ let(:value) { [String.new] }
76
+
77
+ it 'does freeze inner values' do
78
+ subject.first.should be_frozen
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Adamantium::Freezer::Deep, '.freeze' do
4
+ subject { object.freeze(value) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ let(:value) { mock('Value') }
9
+
10
+ it 'should deep freeze value' do
11
+ IceNine.should_receive(:deep_freeze).with(value).and_return(value)
12
+ should be(value)
13
+ end
14
+ end
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Adamantium, '.freeze_object' do
6
- subject { object.freeze_object(value) }
5
+ describe Adamantium::Freezer::Flat, '.call' do
6
+ subject { object.call(value) }
7
7
 
8
8
  let(:object) { self.class.described_type }
9
9
 
@@ -54,4 +54,12 @@ describe Adamantium, '.freeze_object' do
54
54
 
55
55
  it { should be_frozen }
56
56
  end
57
+
58
+ context 'with a composed value' do
59
+ let(:value) { [String.new] }
60
+
61
+ it 'does NOT freeze inner values' do
62
+ subject.first.should_not be_frozen
63
+ end
64
+ end
57
65
  end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Adamantium::Freezer::Flat, '.freeze' do
4
+ subject { object.freeze(value) }
5
+
6
+ let(:object) { described_class }
7
+
8
+ let(:value) { mock('Value') }
9
+
10
+ it 'should freeze value' do
11
+ value.should_receive(:freeze).and_return(value)
12
+ should be(value)
13
+ end
14
+ end
@@ -7,8 +7,8 @@ describe Adamantium, '#memoize' do
7
7
  subject { object.memoize(method, value) }
8
8
 
9
9
  let(:described_class) { Class.new(AdamantiumSpecs::Object) }
10
- let(:object) { described_class.new }
11
- let(:method) { :test }
10
+ let(:object) { described_class.new }
11
+ let(:method) { :test }
12
12
 
13
13
  before do
14
14
  described_class.memoize(method)
@@ -7,9 +7,9 @@ describe Adamantium, '#memoized' do
7
7
  subject { object.memoized(method) }
8
8
 
9
9
  let(:described_class) { Class.new(AdamantiumSpecs::Object) }
10
- let(:method) { :test }
11
- let(:value) { String.new.freeze }
12
- let(:object) { described_class.new }
10
+ let(:method) { :test }
11
+ let(:value) { String.new.freeze }
12
+ let(:object) { described_class.new }
13
13
 
14
14
  before do
15
15
  described_class.memoize(method)
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Adamantium::ModuleMethods, '#freezer' do
4
+ let(:object) do
5
+ Class.new do
6
+ include Adamantium
7
+ end
8
+ end
9
+
10
+ subject { object.freezer }
11
+
12
+ it { should be(Adamantium::Freezer::Deep) }
13
+
14
+ it_should_behave_like 'an idempotent method'
15
+ end
@@ -10,19 +10,29 @@ shared_examples_for 'memoizes method' do
10
10
  instance.send(method).should equal(instance.send(method))
11
11
  end
12
12
 
13
- it 'creates a method that returns a frozen value' do
13
+ it 'creates a method that returns a same value' do
14
14
  subject
15
- object.new.send(method).should be_frozen
15
+ instance = object.new
16
+ first = instance.send(method)
17
+ second = instance.send(method)
18
+ first.should be(second)
16
19
  end
17
20
 
18
21
  specification = proc do
19
22
  subject
20
- file, line = object.new.send(method).first.split(':')[0, 2]
21
- File.expand_path(file).should eql(File.expand_path('../../../../../lib/adamantium.rb', __FILE__))
22
- line.to_i.should eql(211)
23
+ if method != :some_state
24
+ file, line = object.new.send(method).first.split(':')[0, 2]
25
+ File.expand_path(file).should eql(File.expand_path('../../../../../lib/adamantium/module_methods.rb', __FILE__))
26
+ line.to_i.should eql(80)
27
+ end
23
28
  end
24
29
 
25
30
  it 'sets the file and line number properly' do
31
+ # Exclude example for methot that does not return caller
32
+ if method == :some_method
33
+ return
34
+ end
35
+
26
36
  if RUBY_PLATFORM.include?('java')
27
37
  pending('Kernel#caller returns the incorrect line number in JRuby', &specification)
28
38
  else
@@ -49,9 +59,45 @@ shared_examples_for 'memoizes method' do
49
59
  end
50
60
 
51
61
  describe Adamantium::ModuleMethods, '#memoize' do
52
- subject { object.memoize(method) }
62
+ subject { object.memoize(method, options) }
63
+ let(:options) { {} }
64
+
65
+ let(:object) do
66
+ Class.new(AdamantiumSpecs::Object) do
67
+ def some_state
68
+ Object.new
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'with :noop freezer option' do
74
+ let(:method) { :some_state }
75
+ let(:options) { { :freezer => :noop } }
76
+
77
+ it_should_behave_like 'a command method'
78
+ it_should_behave_like 'memoizes method'
79
+
80
+ it 'is still a public method' do
81
+ should be_public_method_defined(method)
82
+ end
83
+
84
+ it 'creates a method that returns a non frozen value' do
85
+ subject
86
+ object.new.send(method).should_not be_frozen
87
+ end
88
+ end
53
89
 
54
- let(:object) { Class.new(AdamantiumSpecs::Object) }
90
+ context 'memoized method that returns generated values' do
91
+ let(:method) { :some_state }
92
+
93
+ it_should_behave_like 'a command method'
94
+ it_should_behave_like 'memoizes method'
95
+
96
+ it 'creates a method that returns a frozen value' do
97
+ subject
98
+ object.new.send(method).should be_frozen
99
+ end
100
+ end
55
101
 
56
102
  context 'public method' do
57
103
  let(:method) { :public_method }
@@ -62,6 +108,11 @@ describe Adamantium::ModuleMethods, '#memoize' do
62
108
  it 'is still a public method' do
63
109
  should be_public_method_defined(method)
64
110
  end
111
+
112
+ it 'creates a method that returns a frozen value' do
113
+ subject
114
+ object.new.send(method).should be_frozen
115
+ end
65
116
  end
66
117
 
67
118
  context 'protected method' do
@@ -73,6 +124,11 @@ describe Adamantium::ModuleMethods, '#memoize' do
73
124
  it 'is still a protected method' do
74
125
  should be_protected_method_defined(method)
75
126
  end
127
+
128
+ it 'creates a method that returns a frozen value' do
129
+ subject
130
+ object.new.send(method).should be_frozen
131
+ end
76
132
  end
77
133
 
78
134
  context 'private method' do
@@ -84,5 +140,10 @@ describe Adamantium::ModuleMethods, '#memoize' do
84
140
  it 'is still a private method' do
85
141
  should be_private_method_defined(method)
86
142
  end
143
+
144
+ it 'creates a method that returns a frozen value' do
145
+ subject
146
+ object.new.send(method).should be_frozen
147
+ end
87
148
  end
88
149
  end
@@ -5,5 +5,5 @@ task :ci => %w[ ci:metrics metrics:heckle ]
5
5
 
6
6
  namespace :ci do
7
7
  desc 'Run metrics (except heckle) and spec'
8
- task :metrics => %w[ spec metrics:verify_measurements metrics:flog metrics:flay metrics:reek metrics:roodi ]
8
+ task :metrics => %w[ spec metrics:verify_measurements metrics:flog metrics:flay metrics:reek metrics:roodi metrics:all ]
9
9
  end
@@ -47,7 +47,7 @@ begin
47
47
  map = NameMap.new
48
48
 
49
49
  heckle_caught_modules = Hash.new { |hash, key| hash[key] = [] }
50
- unhandled_mutations = 0
50
+ uncovered_methods = 0
51
51
 
52
52
  ObjectSpace.each_object(Module) do |mod|
53
53
  next unless mod.name =~ /\A#{root_module_regexp}(?::|\z)/
@@ -167,20 +167,19 @@ begin
167
167
  case line = line.chomp
168
168
  when "The following mutations didn't cause test failures:"
169
169
  heckle_caught_modules[mod.name] << method
170
- when '+++ mutation'
171
- unhandled_mutations += 1
170
+ uncovered_methods += 1
172
171
  end
173
172
  end
174
173
  end
175
174
  end
176
175
  end
177
176
 
178
- if unhandled_mutations > 0
177
+ if uncovered_methods > 0
179
178
  error_message_lines = [ "*************\n" ]
180
179
 
181
- error_message_lines << "Heckle found #{unhandled_mutations} " \
182
- "mutation#{"s" unless unhandled_mutations == 1} " \
183
- "that didn't cause spec violations\n"
180
+ error_message_lines << "Heckle found #{uncovered_methods} " \
181
+ "method#{"s" unless uncovered_methods == 1} " \
182
+ "where mutations didn't cause spec violations\n"
184
183
 
185
184
  heckle_caught_modules.each do |mod, methods|
186
185
  error_message_lines << "#{mod} contains the following " \
@@ -201,7 +200,9 @@ begin
201
200
  end
202
201
  end
203
202
  rescue LoadError
204
- task :heckle => :coverage do
205
- $stderr.puts 'Heckle or mspec is not available. In order to run heckle, you must: gem install heckle mspec'
203
+ namespace :metrics do
204
+ task :heckle => :coverage do
205
+ $stderr.puts 'Heckle or mspec is not available. In order to run heckle, you must: gem install heckle mspec'
206
+ end
206
207
  end
207
208
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adamantium
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Dan Kubb
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-10-26 00:00:00 Z
19
+ date: 2012-11-14 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: backports
@@ -128,7 +128,11 @@ files:
128
128
  - config/site.reek
129
129
  - config/yardstick.yml
130
130
  - lib/adamantium.rb
131
+ - lib/adamantium/class_methods.rb
132
+ - lib/adamantium/freezer.rb
133
+ - lib/adamantium/module_methods.rb
131
134
  - lib/adamantium/version.rb
135
+ - spec/integration/adamantium_spec.rb
132
136
  - spec/rcov.opts
133
137
  - spec/shared/command_method_behavior.rb
134
138
  - spec/shared/each_method_behaviour.rb
@@ -138,13 +142,20 @@ files:
138
142
  - spec/spec.opts
139
143
  - spec/spec_helper.rb
140
144
  - spec/support/config_alias.rb
141
- - spec/unit/adamantium/class_methods/freeze_object_spec.rb
142
145
  - spec/unit/adamantium/class_methods/new_spec.rb
143
146
  - spec/unit/adamantium/dup_spec.rb
144
147
  - spec/unit/adamantium/fixtures/classes.rb
148
+ - spec/unit/adamantium/flat/freezer_spec.rb
145
149
  - spec/unit/adamantium/freeze_spec.rb
150
+ - spec/unit/adamantium/freezer/class_methods/get_spec.rb
151
+ - spec/unit/adamantium/freezer/class_methods/parse_spec.rb
152
+ - spec/unit/adamantium/freezer/deep/class_methods/call_spec.rb
153
+ - spec/unit/adamantium/freezer/deep/class_methods/freeze_spec.rb
154
+ - spec/unit/adamantium/freezer/flat/class_methods/call_spec.rb
155
+ - spec/unit/adamantium/freezer/flat/class_methods/freeze_spec.rb
146
156
  - spec/unit/adamantium/memoize_spec.rb
147
157
  - spec/unit/adamantium/memoized_spec.rb
158
+ - spec/unit/adamantium/module_methods/freezer_spec.rb
148
159
  - spec/unit/adamantium/module_methods/included_spec.rb
149
160
  - spec/unit/adamantium/module_methods/memoize_spec.rb
150
161
  - tasks/metrics/ci.rake
@@ -191,13 +202,21 @@ signing_key:
191
202
  specification_version: 3
192
203
  summary: Immutable extensions to objects
193
204
  test_files:
194
- - spec/unit/adamantium/class_methods/freeze_object_spec.rb
205
+ - spec/integration/adamantium_spec.rb
195
206
  - spec/unit/adamantium/class_methods/new_spec.rb
196
207
  - spec/unit/adamantium/dup_spec.rb
197
208
  - spec/unit/adamantium/fixtures/classes.rb
209
+ - spec/unit/adamantium/flat/freezer_spec.rb
198
210
  - spec/unit/adamantium/freeze_spec.rb
211
+ - spec/unit/adamantium/freezer/class_methods/get_spec.rb
212
+ - spec/unit/adamantium/freezer/class_methods/parse_spec.rb
213
+ - spec/unit/adamantium/freezer/deep/class_methods/call_spec.rb
214
+ - spec/unit/adamantium/freezer/deep/class_methods/freeze_spec.rb
215
+ - spec/unit/adamantium/freezer/flat/class_methods/call_spec.rb
216
+ - spec/unit/adamantium/freezer/flat/class_methods/freeze_spec.rb
199
217
  - spec/unit/adamantium/memoize_spec.rb
200
218
  - spec/unit/adamantium/memoized_spec.rb
219
+ - spec/unit/adamantium/module_methods/freezer_spec.rb
201
220
  - spec/unit/adamantium/module_methods/included_spec.rb
202
221
  - spec/unit/adamantium/module_methods/memoize_spec.rb
203
222
  has_rdoc: