hamsterdam 1.0.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.
- data/CHANGELOG +5 -0
- data/Gemfile +4 -0
- data/README.md +11 -0
- data/Rakefile +12 -0
- data/hamsterdam.gemspec +24 -0
- data/lib/hamsterdam.rb +86 -0
- data/rake_tasks/rspec.rake +25 -0
- data/spec/hamsterdam_spec.rb +133 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/hamsterdam_helpers.rb +3 -0
- metadata +138 -0
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
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" ]
|
data/hamsterdam.gemspec
ADDED
|
@@ -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
|
data/spec/spec_helper.rb
ADDED
|
@@ -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
|
+
|
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
|