hamster 0.2.13 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,9 @@
1
+ === 0.3.0 / 2010-03-28
2
+
3
+ * Add Hamster::Immutable as general purpose module to facililate immutability.
4
+
5
+ * List, Stack, Set, and Hash now all implement Hamster::Immutable.
6
+
1
7
  === 0.2.13 / 2010-03-25
2
8
 
3
9
  * Implement Hash#hash.
@@ -1,5 +1,6 @@
1
1
  require 'forwardable'
2
2
 
3
+ require 'hamster/immutable'
3
4
  require 'hamster/trie'
4
5
 
5
6
  module Hamster
@@ -12,8 +13,10 @@ module Hamster
12
13
 
13
14
  extend Forwardable
14
15
 
15
- def initialize(trie = EmptyTrie)
16
- @trie = trie
16
+ include Immutable
17
+
18
+ def initialize
19
+ @trie = EmptyTrie
17
20
  end
18
21
 
19
22
  def size
@@ -40,16 +43,12 @@ module Hamster
40
43
  def_delegator :self, :get, :[]
41
44
 
42
45
  def put(key, value)
43
- self.class.new(@trie.put(key, value))
46
+ transform { @trie = @trie.put(key, value) }
44
47
  end
45
48
 
46
49
  def delete(key)
47
50
  trie = @trie.delete(key)
48
- if trie.equal?(@trie)
49
- self
50
- else
51
- self.class.new(trie)
52
- end
51
+ transform_unless(trie.equal?(@trie)) { @trie = trie }
53
52
  end
54
53
 
55
54
  def each
@@ -61,7 +60,7 @@ module Hamster
61
60
  def map
62
61
  return self unless block_given?
63
62
  return self if empty?
64
- self.class.new(@trie.reduce(EmptyTrie) { |trie, entry| trie.put(*yield(entry.key, entry.value)) })
63
+ transform { @trie = @trie.reduce(EmptyTrie) { |trie, entry| trie.put(*yield(entry.key, entry.value)) } }
65
64
  end
66
65
  def_delegator :self, :map, :collect
67
66
 
@@ -76,11 +75,7 @@ module Hamster
76
75
  def filter
77
76
  return self unless block_given?
78
77
  trie = @trie.filter { |entry| yield(entry.key, entry.value) }
79
- if trie.equal?(@trie)
80
- self
81
- else
82
- self.class.new(trie)
83
- end
78
+ transform_unless(trie.equal?(@trie)) { @trie = trie }
84
79
  end
85
80
  def_delegator :self, :filter, :select
86
81
  def_delegator :self, :filter, :find_all
@@ -132,10 +127,6 @@ module Hamster
132
127
  reduce(0) { |h, key, value| h ^ key.hash }
133
128
  end
134
129
 
135
- def dup
136
- self
137
- end
138
- def_delegator :self, :dup, :clone
139
130
  def_delegator :self, :dup, :uniq
140
131
  def_delegator :self, :dup, :nub
141
132
  def_delegator :self, :dup, :remove_duplicates
@@ -0,0 +1,52 @@
1
+ require 'forwardable'
2
+
3
+ module Hamster
4
+
5
+ module Immutable
6
+
7
+ def self.included(klass)
8
+ klass.extend(ClassMethods)
9
+ klass.instance_eval do
10
+ include InstanceMethods
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def new(*args)
17
+ super.freeze
18
+ end
19
+
20
+ end
21
+
22
+ module InstanceMethods
23
+
24
+ extend Forwardable
25
+
26
+ def immutable?
27
+ frozen?
28
+ end
29
+
30
+ alias_method :__copy__, :dup
31
+ private :__copy__
32
+
33
+ def dup
34
+ self
35
+ end
36
+ def_delegator :self, :dup, :clone
37
+
38
+ protected
39
+
40
+ def transform_unless(condition, &block)
41
+ condition ? self : transform(&block)
42
+ end
43
+
44
+ def transform(&block)
45
+ __copy__.tap { |copy| copy.instance_eval(&block) }.freeze
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -534,29 +534,8 @@ module Hamster
534
534
  def_delegator :target, :tail
535
535
  def_delegator :target, :empty?
536
536
 
537
- def head
538
- target.head
539
- end
540
-
541
- def tail
542
- target.tail
543
- end
544
-
545
- def empty?
546
- target.empty?
547
- end
548
-
549
537
  protected
550
538
 
551
- def target
552
- # vivify
553
- list = vivify
554
- while list.is_a?(Stream)
555
- list = list.vivify
556
- end
557
- list
558
- end
559
-
560
539
  def vivify
561
540
  @lock.synchronize do
562
541
  unless @block.nil?
@@ -567,6 +546,16 @@ module Hamster
567
546
  @target
568
547
  end
569
548
 
549
+ private
550
+
551
+ def target
552
+ list = vivify
553
+ while list.is_a?(Stream)
554
+ list = list.vivify
555
+ end
556
+ list
557
+ end
558
+
570
559
  end
571
560
 
572
561
  module EmptyList
@@ -1,5 +1,6 @@
1
1
  require 'forwardable'
2
2
 
3
+ require 'hamster/immutable'
3
4
  require 'hamster/undefined'
4
5
  require 'hamster/tuple'
5
6
  require 'hamster/sorter'
@@ -16,8 +17,10 @@ module Hamster
16
17
 
17
18
  extend Forwardable
18
19
 
19
- def initialize(trie = EmptyTrie)
20
- @trie = trie
20
+ include Immutable
21
+
22
+ def initialize
23
+ @trie = EmptyTrie
21
24
  end
22
25
 
23
26
  def empty?
@@ -38,15 +41,13 @@ module Hamster
38
41
  def_delegator :self, :include?, :elem?
39
42
 
40
43
  def add(item)
41
- return self if include?(item)
42
- self.class.new(@trie.put(item, nil))
44
+ transform_unless(include?(item)) { @trie = @trie.put(item, nil) }
43
45
  end
44
46
  def_delegator :self, :add, :<<
45
47
 
46
48
  def delete(item)
47
49
  trie = @trie.delete(item)
48
- return self if trie.equal?(@trie)
49
- self.class.new(trie)
50
+ transform_unless(trie.equal?(@trie)) { @trie = trie }
50
51
  end
51
52
 
52
53
  def each
@@ -58,7 +59,7 @@ module Hamster
58
59
  def map
59
60
  return self unless block_given?
60
61
  return self if empty?
61
- self.class.new(@trie.reduce(EmptyTrie) { |trie, entry| trie.put(yield(entry.key), nil) })
62
+ transform { @trie = @trie.reduce(EmptyTrie) { |trie, entry| trie.put(yield(entry.key), nil) } }
62
63
  end
63
64
  def_delegator :self, :map, :collect
64
65
 
@@ -78,7 +79,7 @@ module Hamster
78
79
  trie = @trie.filter { |entry| yield(entry.key) }
79
80
  return self if trie.equal?(@trie)
80
81
  return EmptySet if trie.empty?
81
- self.class.new(trie)
82
+ transform { @trie = trie }
82
83
  end
83
84
  def_delegator :self, :filter, :select
84
85
  def_delegator :self, :filter, :find_all
@@ -186,24 +187,21 @@ module Hamster
186
187
  next trie if trie.has_key?(item)
187
188
  trie.put(item, nil)
188
189
  end
189
- return self if trie.equal?(@trie)
190
- self.class.new(trie)
190
+ transform_unless(trie.equal?(@trie)) { @trie = trie }
191
191
  end
192
192
  def_delegator :self, :union, :|
193
193
  def_delegator :self, :union, :+
194
194
 
195
195
  def intersection(other)
196
196
  trie = @trie.filter { |entry| other.include?(entry.key) }
197
- return self if trie.equal?(@trie)
198
- self.class.new(trie)
197
+ transform_unless(trie.equal?(@trie)) { @trie = trie }
199
198
  end
200
199
  def_delegator :self, :intersection, :intersect
201
200
  def_delegator :self, :intersection, :&
202
201
 
203
202
  def difference(other)
204
203
  trie = @trie.filter { |entry| !other.include?(entry.key) }
205
- return self if trie.equal?(@trie)
206
- self.class.new(trie)
204
+ transform_unless(trie.equal?(@trie)) { @trie = trie }
207
205
  end
208
206
  def_delegator :self, :difference, :diff
209
207
  def_delegator :self, :difference, :subtract
@@ -254,10 +252,6 @@ module Hamster
254
252
  reduce(0) { |h, item| h ^ item.hash }
255
253
  end
256
254
 
257
- def dup
258
- self
259
- end
260
- def_delegator :self, :dup, :clone
261
255
  def_delegator :self, :dup, :uniq
262
256
  def_delegator :self, :dup, :nub
263
257
  def_delegator :self, :dup, :to_set
@@ -1,11 +1,15 @@
1
1
  require 'forwardable'
2
2
 
3
+ require 'hamster/immutable'
4
+
3
5
  module Hamster
4
6
 
5
7
  class Sorter
6
8
 
7
9
  extend Forwardable
8
10
 
11
+ include Immutable
12
+
9
13
  include Enumerable
10
14
 
11
15
  def initialize(collection)
@@ -1,5 +1,6 @@
1
1
  require 'forwardable'
2
2
 
3
+ require 'hamster/immutable'
3
4
  require 'hamster/list'
4
5
 
5
6
  module Hamster
@@ -12,8 +13,10 @@ module Hamster
12
13
 
13
14
  extend Forwardable
14
15
 
15
- def initialize(list)
16
- @list = list
16
+ include Immutable
17
+
18
+ def initialize
19
+ @list = EmptyList
17
20
  end
18
21
 
19
22
  def empty?
@@ -30,7 +33,7 @@ module Hamster
30
33
  end
31
34
 
32
35
  def push(item)
33
- self.class.new(@list.cons(item))
36
+ transform { @list = @list.cons(item) }
34
37
  end
35
38
  def_delegator :self, :push, :<<
36
39
 
@@ -39,7 +42,7 @@ module Hamster
39
42
  if list.empty?
40
43
  EmptyStack
41
44
  else
42
- self.class.new(list)
45
+ transform { @list = list }
43
46
  end
44
47
  end
45
48
 
@@ -54,11 +57,6 @@ module Hamster
54
57
  end
55
58
  def_delegator :self, :eql?, :==
56
59
 
57
- def dup
58
- self
59
- end
60
- def_delegator :self, :dup, :clone
61
-
62
60
  def to_a
63
61
  @list.to_a
64
62
  end
@@ -78,6 +76,6 @@ module Hamster
78
76
 
79
77
  end
80
78
 
81
- EmptyStack = Stack.new(EmptyList)
79
+ EmptyStack = Stack.new
82
80
 
83
81
  end
@@ -1,11 +1,15 @@
1
1
  require 'forwardable'
2
2
 
3
+ require 'hamster/immutable'
4
+
3
5
  module Hamster
4
6
 
5
7
  class Tuple
6
8
 
7
9
  extend Forwardable
8
10
 
11
+ include Immutable
12
+
9
13
  def initialize(*items)
10
14
  @items = items.freeze
11
15
  end
@@ -25,11 +29,6 @@ module Hamster
25
29
  end
26
30
  def_delegator :self, :eql?, :==
27
31
 
28
- def dup
29
- self
30
- end
31
- def_delegator :self, :dup, :clone
32
-
33
32
  def to_ary
34
33
  @items
35
34
  end
@@ -1,5 +1,5 @@
1
1
  module Hamster
2
2
 
3
- VERSION = "0.2.13".freeze
3
+ VERSION = "0.3.0".freeze
4
4
 
5
5
  end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+ require 'hamster/hash'
5
+
6
+ describe Hamster::Hash do
7
+
8
+ it "includes Immutable" do
9
+ Hamster::Hash.should include(Hamster::Immutable)
10
+ end
11
+
12
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+
5
+ describe Hamster::Immutable do
6
+
7
+ class Fixture
8
+ include Hamster::Immutable
9
+ end
10
+
11
+ [:dup, :clone].each do |method|
12
+
13
+ describe "##{method}" do
14
+
15
+ before do
16
+ @original = Fixture.new
17
+ @result = @original.send(method)
18
+ end
19
+
20
+ it "returns self" do
21
+ @result.should equal(@original)
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,58 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+
5
+ describe Hamster::Immutable do
6
+
7
+ describe "#immutable?" do
8
+
9
+ describe "object constructed after its class becomes Immutable" do
10
+
11
+ class Fixture
12
+ include Hamster::Immutable
13
+ end
14
+
15
+ before do
16
+ @fixture = Fixture.new
17
+ end
18
+
19
+ it "returns true" do
20
+ @fixture.should be_immutable
21
+ end
22
+
23
+ end
24
+
25
+ describe "object constructed before its class becomes Immutable" do
26
+
27
+ before do
28
+ @fixture = Class.new.new
29
+ @fixture.class.instance_eval do
30
+ include Hamster::Immutable
31
+ end
32
+ end
33
+
34
+ describe "that are not frozen" do
35
+
36
+ it "returns false" do
37
+ @fixture.should_not be_immutable
38
+ end
39
+
40
+ end
41
+
42
+ describe "that are frozen" do
43
+
44
+ before do
45
+ @fixture.freeze
46
+ end
47
+
48
+ it "returns true" do
49
+ @fixture.should be_immutable
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+
5
+ describe Hamster::Immutable do
6
+
7
+ describe "#new" do
8
+
9
+ class Person < Struct.new(:first, :last)
10
+ include Hamster::Immutable
11
+ end
12
+
13
+ before do
14
+ @instance = Person.new("Simon", "Harris")
15
+ end
16
+
17
+ it "passes the constructor arguments" do
18
+ @instance.first.should == "Simon"
19
+ @instance.last.should == "Harris"
20
+ end
21
+
22
+ it "freezes the instance" do
23
+ @instance.should be_frozen
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+
5
+ describe Hamster::Immutable do
6
+
7
+ describe "#transform" do
8
+
9
+ class Person < Struct.new(:first, :last)
10
+ include Hamster::Immutable
11
+ public :transform
12
+ end
13
+
14
+ before do
15
+ @original = Person.new("Simon", "Harris")
16
+ @result = @original.transform { self.first = "Sampy" }
17
+ end
18
+
19
+ it "preserves the original" do
20
+ @original.first.should == "Simon"
21
+ @original.last.should == "Harris"
22
+ end
23
+
24
+ it "returns a new instance with the updated values" do
25
+ @result.first.should == "Sampy"
26
+ @result.last.should == "Harris"
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,55 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+
5
+ describe Hamster::Immutable do
6
+
7
+ describe "#transform_unless" do
8
+
9
+ class Person < Struct.new(:first, :last)
10
+ include Hamster::Immutable
11
+ public :transform_unless
12
+ end
13
+
14
+ before do
15
+ @original = Person.new("Simon", "Harris")
16
+ end
17
+
18
+ describe "when the condition is false" do
19
+
20
+ before do
21
+ @result = @original.transform_unless(false) { self.first = "Sampy" }
22
+ end
23
+
24
+ it "preserves the original" do
25
+ @original.first.should == "Simon"
26
+ @original.last.should == "Harris"
27
+ end
28
+
29
+ it "returns a new instance with the updated values" do
30
+ @result.first.should == "Sampy"
31
+ @result.last.should == "Harris"
32
+ end
33
+
34
+ end
35
+
36
+ describe "when the condition is true" do
37
+
38
+ before do
39
+ @result = @original.transform_unless(true) { fail("Should never be called") }
40
+ end
41
+
42
+ it "preserves the original" do
43
+ @original.first.should == "Simon"
44
+ @original.last.should == "Harris"
45
+ end
46
+
47
+ it "returns the original" do
48
+ @result.should equal(@original)
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+ require 'hamster/set'
5
+
6
+ describe Hamster::Set do
7
+
8
+ it "includes Immutable" do
9
+ Hamster::Set.should include(Hamster::Immutable)
10
+ end
11
+
12
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+ require 'hamster/sorter'
5
+
6
+ describe Hamster::Sorter do
7
+
8
+ it "includes Immutable" do
9
+ Hamster::Sorter.should include(Hamster::Immutable)
10
+ end
11
+
12
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+ require 'hamster/stack'
5
+
6
+ describe Hamster::Stack do
7
+
8
+ it "includes Immutable" do
9
+ Hamster::Stack.should include(Hamster::Immutable)
10
+ end
11
+
12
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ require 'hamster/immutable'
4
+ require 'hamster/tuple'
5
+
6
+ describe Hamster::Tuple do
7
+
8
+ it "includes Immutable" do
9
+ Hamster::Tuple.should include(Hamster::Immutable)
10
+ end
11
+
12
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 13
9
- version: 0.2.13
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Simon Harris
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-25 00:00:00 +11:00
17
+ date: 2010-03-28 00:00:00 +11:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -60,6 +60,7 @@ files:
60
60
  - lib/hamster/core_ext/io.rb
61
61
  - lib/hamster/core_ext.rb
62
62
  - lib/hamster/hash.rb
63
+ - lib/hamster/immutable.rb
63
64
  - lib/hamster/list.rb
64
65
  - lib/hamster/set.rb
65
66
  - lib/hamster/sorter.rb
@@ -86,6 +87,7 @@ files:
86
87
  - spec/hamster/hash/get_spec.rb
87
88
  - spec/hamster/hash/has_key_spec.rb
88
89
  - spec/hamster/hash/hash_spec.rb
90
+ - spec/hamster/hash/immutable_spec.rb
89
91
  - spec/hamster/hash/inspect_spec.rb
90
92
  - spec/hamster/hash/keys_spec.rb
91
93
  - spec/hamster/hash/map_spec.rb
@@ -95,6 +97,11 @@ files:
95
97
  - spec/hamster/hash/remove_spec.rb
96
98
  - spec/hamster/hash/size_spec.rb
97
99
  - spec/hamster/hash/uniq_spec.rb
100
+ - spec/hamster/immutable/copying_spec.rb
101
+ - spec/hamster/immutable/immutable_spec.rb
102
+ - spec/hamster/immutable/new_spec.rb
103
+ - spec/hamster/immutable/transform_spec.rb
104
+ - spec/hamster/immutable/transform_unless_spec.rb
98
105
  - spec/hamster/list/all_spec.rb
99
106
  - spec/hamster/list/any_spec.rb
100
107
  - spec/hamster/list/append_spec.rb
@@ -182,6 +189,7 @@ files:
182
189
  - spec/hamster/set/group_by_spec.rb
183
190
  - spec/hamster/set/hash_spec.rb
184
191
  - spec/hamster/set/head_spec.rb
192
+ - spec/hamster/set/immutable_spec.rb
185
193
  - spec/hamster/set/include_spec.rb
186
194
  - spec/hamster/set/inspect_spec.rb
187
195
  - spec/hamster/set/intersection_spec.rb
@@ -205,11 +213,13 @@ files:
205
213
  - spec/hamster/set/to_set_spec.rb
206
214
  - spec/hamster/set/union_spec.rb
207
215
  - spec/hamster/set/uniq_spec.rb
216
+ - spec/hamster/sorter/immutable_spec.rb
208
217
  - spec/hamster/stack/clear_spec.rb
209
218
  - spec/hamster/stack/construction_spec.rb
210
219
  - spec/hamster/stack/copying_spec.rb
211
220
  - spec/hamster/stack/empty_spec.rb
212
221
  - spec/hamster/stack/eql_spec.rb
222
+ - spec/hamster/stack/immutable_spec.rb
213
223
  - spec/hamster/stack/inspect_spec.rb
214
224
  - spec/hamster/stack/pop_spec.rb
215
225
  - spec/hamster/stack/push_spec.rb
@@ -222,6 +232,7 @@ files:
222
232
  - spec/hamster/tuple/copying_spec.rb
223
233
  - spec/hamster/tuple/eql_spec.rb
224
234
  - spec/hamster/tuple/first_spec.rb
235
+ - spec/hamster/tuple/immutable_spec.rb
225
236
  - spec/hamster/tuple/inspect_spec.rb
226
237
  - spec/hamster/tuple/last_spec.rb
227
238
  - spec/hamster/tuple/to_a_spec.rb