hamsterdam 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ Hamsterdam v1.0.0
2
+ * Can define immutable record types via Hamsterdam::Struct.define(*field_names)
3
+ * Provides getters for all fields, and set_* transformers that return the updated version of the record.
4
+ * Handy merge function for updating one or more fields by name
5
+ * Wraps Hamaster's (https://github.com/harukizaemon/hamster) immutable Hamster::Hash class
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Deps are in hamsterdam.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Hamsterdam #
2
+
3
+ Immutable Struct-like record structures based on Hamster's (https://github.com/harukizaemon/hamster) immutable Hashes. Convenient methods for updating record structures and returning new immutable instances.
4
+
5
+ # Example #
6
+
7
+ Person = Hamsterdam::Struct.define(:name, :address, :age)
8
+ david = Person.new(name: "David", age: true, address: "Coopersville")
9
+ david1 = david.set_address("East Grand Rapids")
10
+ david2 = david.merge(name: "Crosby", age: "increased")
11
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ HERE = File.dirname(__FILE__)
4
+
5
+ Dir[File.expand_path(HERE) + "/rake_tasks/*.rake"].each do |rake_file|
6
+ import rake_file
7
+ end
8
+
9
+ # desc 'Default: run specs and cucumber features'
10
+ # task :default => [ "spec", "cuc:features" ]
11
+
12
+ task :default => [ "spec" ]
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/lib")
3
+ require File.expand_path('../lib/hamsterdam', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["David Crosby"]
7
+ gem.email = ["david.crosby@atomicobject.com"]
8
+ gem.description = %q{Immutable Struct-like record structures based on Hamster.}
9
+ gem.summary = %q{Immutable Struct-like record structures based on Hamster.}
10
+ gem.homepage = "https://github.com/atomicobject/hamsterdam"
11
+
12
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ gem.files = `git ls-files`.split("\n") - [".gitignore", ".rspec", ".rvmrc", "NOTES.txt", "TODO"]
14
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ gem.name = "hamsterdam"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Hamsterdam::VERSION
18
+
19
+ gem.add_dependency "hamster"
20
+ gem.add_development_dependency "rake"
21
+ gem.add_development_dependency "rspec"
22
+ gem.add_development_dependency "simplecov"
23
+ gem.add_development_dependency "pry"
24
+ end
data/lib/hamsterdam.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'hamster'
2
+
3
+ module Hamsterdam
4
+ VERSION = "1.0.0"
5
+
6
+ class Struct
7
+ def self.define(*field_names)
8
+ struct_class = Class.new(Hamsterdam::Struct) do
9
+ field_names.each do |fname|
10
+ define_method fname do
11
+ return @data[fname]
12
+ end
13
+
14
+ define_method "set_#{fname}" do |value|
15
+ self.class.new(@data.put(fname, value))
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ struct_class.instance_variable_set(:@field_names, Hamster.set(*field_names))
22
+ class << struct_class
23
+ def field_names
24
+ @field_names
25
+ end
26
+ end
27
+ struct_class
28
+ end
29
+
30
+ def initialize(values=Hamster.hash)
31
+ @data = flesh_out(ensure_hamster_hash(values))
32
+ validate_keys(@data)
33
+ end
34
+
35
+ def merge(values)
36
+ self.class.new(@data.merge(ensure_hamster_hash(values)))
37
+ end
38
+
39
+ def ==(other)
40
+ @data == other.to_hamster_hash
41
+ end
42
+
43
+ def eql?(other)
44
+ self.class == other.class && self == other
45
+ end
46
+
47
+ def hash
48
+ @data.hash
49
+ end
50
+
51
+ def to_hamster_hash
52
+ @data
53
+ end
54
+
55
+ private
56
+ def validate_keys(data)
57
+ valid_keys = self.class.field_names
58
+ bad_keys = data.keys - valid_keys
59
+ if bad_keys.any?
60
+ raise "#{self.class.name || "Anonymous Hamsterdam::Struct"} can't be constructed with #{bad_keys.inspect}. Valid keys: #{valid_keys.inspect}"
61
+ end
62
+ end
63
+
64
+ def ensure_hamster_hash(h)
65
+ case h
66
+ when Hash
67
+ Hamster.hash(h)
68
+ when Hamster::Hash
69
+ h
70
+ else
71
+ raise "Expected Hash or Hamster::Hash. Do not want: #{h.inspect}"
72
+ end
73
+ end
74
+
75
+ def flesh_out(data)
76
+ fnames = self.class.field_names
77
+ miss = fnames - data.keys
78
+ if miss.any?
79
+ return miss.inject(data) { |h,name| h.put(name,nil) }
80
+ else
81
+ return data
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,25 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+ rescue Exception => e
4
+ "RSpec is not available. 'spec' task will not be defined."
5
+ end
6
+
7
+ begin # protect from missing rspec
8
+ desc "Run specs"
9
+ RSpec::Core::RakeTask.new do |t|
10
+ if ENV["file"]
11
+ t.pattern = ENV["file"]
12
+ end
13
+ t.rspec_opts = "--color --format documentation" # --tty
14
+ end
15
+
16
+ desc "Run individual spec"
17
+ task "spec:just" do
18
+ RSpec::Core::RakeTask.new("_tmp_rspec") do |t|
19
+ t.pattern = ENV["file"] || raise("Please supply 'file' argument")
20
+ t.rspec_opts = "--color"
21
+ end
22
+ Rake::Task["_tmp_rspec"].invoke
23
+ end
24
+ rescue Exception => e
25
+ end
@@ -0,0 +1,133 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
2
+
3
+ describe "Hamsterdam structures" do
4
+ def define_hamsterdam_struct(*field_names)
5
+ Hamsterdam::Struct.define(*field_names)
6
+ end
7
+ describe "Struct.define" do
8
+ let(:struct_class) { define_hamsterdam_struct(:top, :bottom) }
9
+
10
+ it "creates a structure class based on the given fields" do
11
+ struct = struct_class.new(top: 200, bottom: "all the way down")
12
+ struct.should be
13
+ struct.top.should == 200
14
+ struct.bottom.should == "all the way down"
15
+ end
16
+
17
+ it "can be built with Hamster hashes" do
18
+ struct = struct_class.new(Hamster.hash(top: 10, bottom: "low"))
19
+ struct.should be
20
+ struct.top.should == 10
21
+ struct.bottom.should == "low"
22
+ end
23
+
24
+ it "does not accept constructor inputs that are not defined" do
25
+ lambda do struct_class.new(oops: ":)", wups: ":(") end.should raise_error(/oops/)
26
+ end
27
+
28
+ it "allows omission of keys" do
29
+ struct = struct_class.new(top: 50)
30
+ struct.top.should == 50
31
+ struct.bottom.should be_nil
32
+ end
33
+
34
+ it "allows omission of all keys" do
35
+ struct = struct_class.new
36
+ struct.top.should be_nil
37
+ struct.bottom.should be_nil
38
+ end
39
+
40
+ it "provides access to the internal data as a Hamster.hash" do
41
+ s1 = struct_class.new(top: 50, bottom: 75)
42
+ s1.to_hamster_hash.should == Hamster.hash(top: 50, bottom: 75)
43
+ end
44
+
45
+ it "raises helpful error when constructed with invalid objects" do
46
+ lambda do struct_class.new("LAWDY") end.should raise_error /Do not want.*LAWDY/
47
+ end
48
+
49
+ describe "equality" do
50
+ it "considers two structs equal if they have the same field values" do
51
+ s1 = struct_class.new(top: 50, bottom: 75)
52
+ s2 = struct_class.new(top: 50, bottom: 75)
53
+ s1.eql?(s2).should == true
54
+ (s1 == s2).should == true
55
+ s1.should == s2
56
+ end
57
+
58
+ it "considers two structs NOT equal if they have the different field values" do
59
+ s1 = struct_class.new(top: 50, bottom: 75)
60
+ s2 = struct_class.new(top: 50, bottom: 74)
61
+ s3 = struct_class.new(top: 51, bottom: 75)
62
+
63
+ s1.eql?(s2).should_not == true
64
+ (s1 == s2).should_not == true
65
+ s1.should_not == s2
66
+
67
+ s1.eql?(s3).should_not == true
68
+ (s1 == s3).should_not == true
69
+ s1.should_not == s3
70
+ end
71
+
72
+ it "doesn't consider to structs eql? unless they are same class" do
73
+ s1 = struct_class.new(top: 50, bottom: 75)
74
+ s2 = define_hamsterdam_struct(:top, :bottom).new(top:50, bottom:75)
75
+ s1.eql?(s2).should == false
76
+ (s1 == s2).should == true # should still be ==
77
+ end
78
+
79
+ it "considers equal two structs if one has missing keys, and the other has nil values for those keys" do
80
+ s1 = struct_class.new(top: 50, bottom: nil)
81
+ s2 = struct_class.new(top: 50)
82
+ #binding.pry
83
+ s1.eql?(s2).should == true
84
+ (s1 == s2).should == true
85
+ s1.should == s2
86
+ end
87
+ end
88
+
89
+ it "uses the same #hash as Hamster::Hash" do
90
+ s1 = struct_class.new(top: 50, bottom: 75)
91
+ s1.hash.should == Hamster.hash(top:50, bottom:75).hash
92
+ end
93
+
94
+ describe "transformation" do
95
+ it "provides setters for individual fields that return an updated version of the struct" do
96
+ struct = struct_class.new(top: 10, bottom: 1)
97
+ struct2 = struct.set_top("woot")
98
+ struct2.top.should == "woot"
99
+ struct2.bottom.should == 1
100
+
101
+ struct.top.should == 10
102
+ struct.bottom.should == 1
103
+ end
104
+
105
+ it "provides a merge function" do
106
+ struct = struct_class.new(top: 10, bottom: 1)
107
+
108
+ struct2 = struct.merge(bottom: "new")
109
+ struct2.top.should == 10
110
+ struct2.bottom.should == "new"
111
+
112
+ struct.top.should == 10
113
+ struct.bottom.should == 1
114
+
115
+ struct3 = struct2.merge(top: "newer", bottom: "very")
116
+ struct3.top.should == "newer"
117
+ struct3.bottom.should == "very"
118
+ end
119
+
120
+ it "can merge-in Hamster::Hash" do
121
+ struct = struct_class.new(top: 10, bottom: 1)
122
+
123
+ struct2 = struct.merge(Hamster.hash(bottom: "newer", top: "newest"))
124
+ struct2.top.should == "newest"
125
+ struct2.bottom.should == "newer"
126
+
127
+ struct.top.should == 10
128
+ struct.bottom.should == 1
129
+ end
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,26 @@
1
+ PROJECT_ROOT = File.expand_path(File.dirname(__FILE__) + "/..")
2
+
3
+ $LOAD_PATH << "#{PROJECT_ROOT}/lib"
4
+ $LOAD_PATH << "#{PROJECT_ROOT}/spec"
5
+
6
+ require 'simplecov' # SimpleCov must come first!
7
+ # This start/config code could alternatively go in .simplecov in project root:
8
+ SimpleCov.start do
9
+ add_filter "/spec/"
10
+ end
11
+
12
+ #ENV["APP_ENV"] = "rspec"
13
+
14
+ require 'hamsterdam'
15
+
16
+ require 'pry'
17
+
18
+ # Load all support files
19
+ Dir["#{PROJECT_ROOT}/spec/support/*.rb"].each do |support|
20
+ require support
21
+ end
22
+
23
+ RSpec.configure do |config|
24
+ config.include HamsterdamHelpers
25
+ end
26
+
@@ -0,0 +1,3 @@
1
+ module HamsterdamHelpers
2
+ #
3
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hamsterdam
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Crosby
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: hamster
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: pry
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Immutable Struct-like record structures based on Hamster.
95
+ email:
96
+ - david.crosby@atomicobject.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - CHANGELOG
102
+ - Gemfile
103
+ - README.md
104
+ - Rakefile
105
+ - hamsterdam.gemspec
106
+ - lib/hamsterdam.rb
107
+ - rake_tasks/rspec.rake
108
+ - spec/hamsterdam_spec.rb
109
+ - spec/spec_helper.rb
110
+ - spec/support/hamsterdam_helpers.rb
111
+ homepage: https://github.com/atomicobject/hamsterdam
112
+ licenses: []
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 1.8.24
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: Immutable Struct-like record structures based on Hamster.
135
+ test_files:
136
+ - spec/hamsterdam_spec.rb
137
+ - spec/spec_helper.rb
138
+ - spec/support/hamsterdam_helpers.rb