hamster 0.2.13 → 0.3.0

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,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