adamantium 0.0.2 → 0.0.3

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