hamsterdam 1.0.5 → 1.0.6
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.
- data/CHANGELOG +3 -0
- data/lib/hamsterdam.rb +73 -21
- data/lib/hamsterdam/clj.rb +77 -0
- data/lib/hamsterdam/version.rb +1 -1
- data/spec/hamsterdam_spec.rb +58 -11
- data/spec/spec_helper.rb +6 -1
- metadata +9 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
Hamsterdam v1.0.5
|
2
|
+
* Added hamsterdam/clj to allow for optionally using the Clojure PersistentHashMap isntead of a Hamster hash as the backing data structure
|
3
|
+
|
1
4
|
Hamsterdam v1.0.5
|
2
5
|
* Setters return the same instance of the object if the value being set is unchanged
|
3
6
|
* Optimized creation of a new struct caused by a setter modification by skipping validation
|
data/lib/hamsterdam.rb
CHANGED
@@ -2,10 +2,67 @@ require 'hamster'
|
|
2
2
|
|
3
3
|
module Hamsterdam
|
4
4
|
|
5
|
+
module Hamster
|
6
|
+
|
7
|
+
def self.hash(*hash)
|
8
|
+
::Hamster.hash(*hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.set(*values)
|
12
|
+
::Hamster.set(*values)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.list(*values)
|
16
|
+
::Hamster.list(*values)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.internal_hash_class
|
20
|
+
::Hamster::Hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.symbolize_keys(hash)
|
24
|
+
hash.reduce(hash) do |memo,k,v|
|
25
|
+
if Symbol === k
|
26
|
+
memo
|
27
|
+
else
|
28
|
+
memo.delete(k).put(k.to_sym, v)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
5
34
|
def self.Struct(*field_names)
|
6
35
|
Hamsterdam::Struct.define(*field_names)
|
7
36
|
end
|
8
37
|
|
38
|
+
def self.internals=(mod)
|
39
|
+
@internal_representation_module = mod
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.internals
|
43
|
+
@internal_representation_module
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.hash(*hash)
|
47
|
+
internals.hash(*hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.set(*values)
|
51
|
+
internals.set(*values)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.list(*values)
|
55
|
+
internals.list(*values)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.internal_hash_class
|
59
|
+
internals.internal_hash_class
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.symbolize_keys(hash)
|
63
|
+
internals.symbolize_keys(hash)
|
64
|
+
end
|
65
|
+
|
9
66
|
class Struct
|
10
67
|
def self.define(*field_names)
|
11
68
|
struct_class = Class.new(Hamsterdam::Struct) do
|
@@ -23,11 +80,10 @@ module Hamsterdam
|
|
23
80
|
end
|
24
81
|
end
|
25
82
|
end
|
26
|
-
|
27
83
|
end
|
28
84
|
|
29
|
-
struct_class.instance_variable_set(:@field_names,
|
30
|
-
struct_class.instance_variable_set(:@field_names_list,
|
85
|
+
struct_class.instance_variable_set(:@field_names, Hamsterdam.set(*field_names))
|
86
|
+
struct_class.instance_variable_set(:@field_names_list, Hamsterdam.list(*field_names))
|
31
87
|
class << struct_class
|
32
88
|
def field_names
|
33
89
|
if !@field_names.nil?
|
@@ -47,9 +103,9 @@ module Hamsterdam
|
|
47
103
|
struct_class
|
48
104
|
end
|
49
105
|
|
50
|
-
def initialize(values=
|
106
|
+
def initialize(values=Hamsterdam.hash, validate=true)
|
51
107
|
if validate
|
52
|
-
@data = flesh_out(
|
108
|
+
@data = flesh_out(ensure_expected_hash(values))
|
53
109
|
validate_keys(@data)
|
54
110
|
else
|
55
111
|
@data = values
|
@@ -57,11 +113,11 @@ module Hamsterdam
|
|
57
113
|
end
|
58
114
|
|
59
115
|
def merge(values)
|
60
|
-
self.class.new(@data.merge(
|
116
|
+
self.class.new(@data.merge(ensure_expected_hash(values)))
|
61
117
|
end
|
62
118
|
|
63
119
|
def ==(other)
|
64
|
-
|
120
|
+
internal_hash == other.internal_hash
|
65
121
|
end
|
66
122
|
|
67
123
|
def eql?(other)
|
@@ -72,9 +128,10 @@ module Hamsterdam
|
|
72
128
|
@data.hash
|
73
129
|
end
|
74
130
|
|
75
|
-
def
|
131
|
+
def internal_hash
|
76
132
|
@data
|
77
133
|
end
|
134
|
+
alias_method :to_hamster_hash, :internal_hash
|
78
135
|
|
79
136
|
def inspect
|
80
137
|
to_s
|
@@ -82,7 +139,7 @@ module Hamsterdam
|
|
82
139
|
|
83
140
|
def to_s
|
84
141
|
name = self.class.name ? self.class.name.split(/::/).last : self.class.to_s
|
85
|
-
data =
|
142
|
+
data = internal_hash
|
86
143
|
fields = self.class.field_names_list.map { |fname| "#{fname}: #{data[fname].inspect}" }
|
87
144
|
"<#{([name]+fields).join(" ")}>"
|
88
145
|
end
|
@@ -90,20 +147,20 @@ module Hamsterdam
|
|
90
147
|
private
|
91
148
|
def validate_keys(data)
|
92
149
|
valid_keys = self.class.field_names
|
93
|
-
bad_keys = data.keys - valid_keys
|
150
|
+
bad_keys = data.keys - valid_keys.to_a
|
94
151
|
if bad_keys.any?
|
95
152
|
raise "#{self.class.name || "Anonymous Hamsterdam::Struct"} can't be constructed with #{bad_keys.inspect}. Valid keys: #{valid_keys.inspect}"
|
96
153
|
end
|
97
154
|
end
|
98
155
|
|
99
|
-
def
|
156
|
+
def ensure_expected_hash(h)
|
100
157
|
case h
|
101
158
|
when Hash
|
102
|
-
|
103
|
-
when
|
159
|
+
Hamsterdam.hash(h)
|
160
|
+
when Hamsterdam.internal_hash_class
|
104
161
|
h
|
105
162
|
else
|
106
|
-
raise "Expected Hash or
|
163
|
+
raise "Expected Hash or #{Hamsterdam.internal_hash_class}. Do not want: #{h.inspect}"
|
107
164
|
end
|
108
165
|
end
|
109
166
|
|
@@ -120,13 +177,8 @@ module Hamsterdam
|
|
120
177
|
end
|
121
178
|
|
122
179
|
def symbolize_keys(data)
|
123
|
-
|
124
|
-
if Symbol === k
|
125
|
-
memo
|
126
|
-
else
|
127
|
-
memo.delete(k).put(k.to_sym, v)
|
128
|
-
end
|
129
|
-
end
|
180
|
+
Hamsterdam.symbolize_keys(data)
|
130
181
|
end
|
131
182
|
end
|
132
183
|
end
|
184
|
+
Hamsterdam.internals = Hamsterdam::Hamster
|
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
class Java::ClojureLang::PersistentHashMap
|
3
|
+
alias_method :put, :assoc
|
4
|
+
alias_method :delete, :without
|
5
|
+
alias_method :==, :equals
|
6
|
+
end
|
7
|
+
|
8
|
+
# java_import 'clojure.lang.PersistentList$EmptyList'
|
9
|
+
# class EmptyList
|
10
|
+
# def inspect
|
11
|
+
# to_a.inspect
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# class Java::ClojureLang::PersistentList
|
16
|
+
# def inspect
|
17
|
+
# to_a.inspect
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# class Java::ClojureLang::PersistentHashSet
|
22
|
+
# def inspect
|
23
|
+
# to_set.inspect
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def -(other)
|
27
|
+
# reject { |e| other.include?(e) }
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# alias_method :add, :cons
|
31
|
+
# end
|
32
|
+
|
33
|
+
module Hamsterdam
|
34
|
+
module Clojure
|
35
|
+
def self.hash(h = nil)
|
36
|
+
if h.nil?
|
37
|
+
Java::ClojureLang::PersistentHashMap::EMPTY
|
38
|
+
else
|
39
|
+
Java::ClojureLang::PersistentHashMap.create(h)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.internal_hash_class
|
44
|
+
Java::ClojureLang::PersistentHashMap
|
45
|
+
end
|
46
|
+
|
47
|
+
# def self.set(*values)
|
48
|
+
# Java::ClojureLang::PersistentHashSet.create(values)
|
49
|
+
# end
|
50
|
+
|
51
|
+
# def self.list(*values)
|
52
|
+
# values.reverse.inject(Java::ClojureLang::PersistentList::EMPTY) do |list, value|
|
53
|
+
# list.cons(value)
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
|
57
|
+
def self.set(*values)
|
58
|
+
::Hamster.set(*values)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.list(*values)
|
62
|
+
::Hamster.list(*values)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.symbolize_keys(hash)
|
66
|
+
hash.reduce(hash) do |memo,(k,v)|
|
67
|
+
if Symbol === k
|
68
|
+
memo
|
69
|
+
else
|
70
|
+
memo.delete(k).put(k.to_sym, v)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Hamsterdam.internals = Hamsterdam::Clojure
|
data/lib/hamsterdam/version.rb
CHANGED
data/spec/hamsterdam_spec.rb
CHANGED
@@ -7,6 +7,53 @@ describe "Hamsterdam structures" do
|
|
7
7
|
|
8
8
|
let(:struct_class) { define_hamsterdam_struct(:top, :bottom) }
|
9
9
|
|
10
|
+
describe "immutable data structure helpers" do
|
11
|
+
describe "#hash" do
|
12
|
+
it "provides an empty hash" do
|
13
|
+
h = Hamsterdam.hash
|
14
|
+
h.should be_empty
|
15
|
+
h.class.should == Hamsterdam.internal_hash_class
|
16
|
+
h.should == Hamsterdam.hash
|
17
|
+
end
|
18
|
+
|
19
|
+
it "provides a hash populated with passed in key/values" do
|
20
|
+
h = Hamsterdam.hash(a: "1", b: "2")
|
21
|
+
h[:a].should == "1"
|
22
|
+
h[:b].should == "2"
|
23
|
+
h.class.should == Hamsterdam.internal_hash_class
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#set" do
|
28
|
+
it "provides an empty set" do
|
29
|
+
s = Hamsterdam.set
|
30
|
+
s.should be_empty
|
31
|
+
end
|
32
|
+
|
33
|
+
it "provides a set populated with passed values" do
|
34
|
+
s = Hamsterdam.set("a", "b", "c", "a")
|
35
|
+
s.should_not be_empty
|
36
|
+
s.should have(3).entries
|
37
|
+
s.should include("a", "b", "c")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#list" do
|
42
|
+
it "provides an empty list" do
|
43
|
+
l = Hamsterdam.list
|
44
|
+
l.should be_empty
|
45
|
+
end
|
46
|
+
|
47
|
+
it "provides a list populated with passed values" do
|
48
|
+
l = Hamsterdam.list("a", "b", "c", "a")
|
49
|
+
l.should_not be_empty
|
50
|
+
l.should have(4).items
|
51
|
+
l.should include("a", "b", "c")
|
52
|
+
l.to_a.should == ["a", "b", "c", "a"]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
10
57
|
describe "Struct.define" do
|
11
58
|
|
12
59
|
it "creates a structure class based on the given fields" do
|
@@ -16,8 +63,8 @@ describe "Hamsterdam structures" do
|
|
16
63
|
struct.bottom.should == "all the way down"
|
17
64
|
end
|
18
65
|
|
19
|
-
it "can be built with
|
20
|
-
struct = struct_class.new(
|
66
|
+
it "can be built with underlying persistent data structure hashes hashes" do
|
67
|
+
struct = struct_class.new(Hamsterdam.hash(top: 10, bottom: "low"))
|
21
68
|
struct.should be
|
22
69
|
struct.top.should == 10
|
23
70
|
struct.bottom.should == "low"
|
@@ -39,9 +86,9 @@ describe "Hamsterdam structures" do
|
|
39
86
|
struct.bottom.should be_nil
|
40
87
|
end
|
41
88
|
|
42
|
-
it "provides access to the internal data as
|
89
|
+
it "provides access to the internal data as the correct hash type" do
|
43
90
|
s1 = struct_class.new(top: 50, bottom: 75)
|
44
|
-
s1.
|
91
|
+
s1.internal_hash.should == Hamsterdam.hash(top: 50, bottom: 75)
|
45
92
|
end
|
46
93
|
|
47
94
|
it "raises helpful error when constructed with invalid objects" do
|
@@ -98,16 +145,16 @@ describe "Hamsterdam structures" do
|
|
98
145
|
it "considers equal two structs if one has missing keys, and the other has nil values for those keys" do
|
99
146
|
s1 = struct_class.new(top: 50, bottom: nil)
|
100
147
|
s2 = struct_class.new(top: 50)
|
101
|
-
#binding.pry
|
102
|
-
s1.eql?(s2).should == true
|
148
|
+
# binding.pry
|
103
149
|
(s1 == s2).should == true
|
150
|
+
s1.eql?(s2).should == true
|
104
151
|
s1.should == s2
|
105
152
|
end
|
106
153
|
end
|
107
154
|
|
108
|
-
it "uses the same #hash as
|
155
|
+
it "uses the same #hash as the underlying data structure" do
|
109
156
|
s1 = struct_class.new(top: 50, bottom: 75)
|
110
|
-
s1.hash.should ==
|
157
|
+
s1.hash.should == Hamsterdam.hash(top:50, bottom:75).hash
|
111
158
|
end
|
112
159
|
|
113
160
|
describe "transformation" do
|
@@ -141,10 +188,10 @@ describe "Hamsterdam structures" do
|
|
141
188
|
struct3.bottom.should == "very"
|
142
189
|
end
|
143
190
|
|
144
|
-
it "can merge-in
|
191
|
+
it "can merge-in a non-ruby hash" do
|
145
192
|
struct = struct_class.new(top: 10, bottom: 1)
|
146
193
|
|
147
|
-
struct2 = struct.merge(
|
194
|
+
struct2 = struct.merge(Hamsterdam.hash(bottom: "newer", top: "newest"))
|
148
195
|
struct2.top.should == "newest"
|
149
196
|
struct2.bottom.should == "newer"
|
150
197
|
|
@@ -211,7 +258,7 @@ describe "Hamsterdam structures" do
|
|
211
258
|
val.foo.should == 3
|
212
259
|
val.bar.should == 4
|
213
260
|
|
214
|
-
val.
|
261
|
+
val.internal_hash.should == Hamsterdam.hash(foo: 3, bar: 4)
|
215
262
|
end
|
216
263
|
end
|
217
264
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,9 +11,14 @@ end
|
|
11
11
|
|
12
12
|
#ENV["APP_ENV"] = "rspec"
|
13
13
|
|
14
|
+
require 'pry'
|
14
15
|
require 'hamsterdam'
|
15
16
|
|
16
|
-
|
17
|
+
if ENV['clj'] == 'true'
|
18
|
+
require "#{PROJECT_ROOT}/spec/jars/clojure-1.5.1.jar"
|
19
|
+
require 'hamsterdam/clj'
|
20
|
+
end
|
21
|
+
|
17
22
|
|
18
23
|
# Load all support files
|
19
24
|
Dir["#{PROJECT_ROOT}/spec/support/*.rb"].each do |support|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hamsterdam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-03-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: hamster
|
@@ -100,6 +100,7 @@ extra_rdoc_files: []
|
|
100
100
|
files:
|
101
101
|
- README.md
|
102
102
|
- CHANGELOG
|
103
|
+
- lib/hamsterdam/clj.rb
|
103
104
|
- lib/hamsterdam/version.rb
|
104
105
|
- lib/hamsterdam.rb
|
105
106
|
- spec/hamsterdam_spec.rb
|
@@ -117,12 +118,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
118
|
- - ! '>='
|
118
119
|
- !ruby/object:Gem::Version
|
119
120
|
version: '0'
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
hash: 4489320240594326591
|
120
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
125
|
none: false
|
122
126
|
requirements:
|
123
127
|
- - ! '>='
|
124
128
|
- !ruby/object:Gem::Version
|
125
129
|
version: '0'
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
hash: 4489320240594326591
|
126
133
|
requirements: []
|
127
134
|
rubyforge_project:
|
128
135
|
rubygems_version: 1.8.24
|