value_object_struct 0.6.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/.rspec +1 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +50 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/lib/value_object_struct.rb +68 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/value_object_struct_spec.rb +116 -0
- data/value_object_struct.gemspec +54 -0
- metadata +108 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Riley Lynch, Teleological Software, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
= ValueObjectStruct
|
2
|
+
|
3
|
+
The ValueObjectStruct module uses Ruby's native Struct class to facilitate the
|
4
|
+
declaration of immutable value object classes.
|
5
|
+
|
6
|
+
ValueObjectStruct classes are defined with
|
7
|
+
ValueObjectStruct.class_with_attributes and a list of member attributes
|
8
|
+
specified by Symbol. Like Struct classes, they may be assigned to a constant
|
9
|
+
or variable, or used in a superclass declaration.
|
10
|
+
|
11
|
+
ValueObjectStruct instances are Struct instances with private attribute write
|
12
|
+
access, including both named attribute writers and Hash or Array style
|
13
|
+
bracketed subscript assignment. ValueObjectStruct instances created with
|
14
|
+
::value (as opposed to ::new or ::for_values) are frozen, so that
|
15
|
+
attribute values may not be reassigned after initialization, even by
|
16
|
+
private methods.
|
17
|
+
|
18
|
+
ValueObjectStruct instances do not enforce immutability of attribute values
|
19
|
+
themselves, so it may be desirable to freeze mutable value types such
|
20
|
+
as String, Array and Hash before using them to initialize an instance.
|
21
|
+
|
22
|
+
As Struct instances, ValueObjectStruct instances provide equality and hash
|
23
|
+
semantics via #== and #hash. #empty? returns true for value object
|
24
|
+
instances with no non-nil attribute values.
|
25
|
+
|
26
|
+
ValueObjectStruct instances provide an #attributes and #attribute method
|
27
|
+
for compatibility with ActiveModel. #attributes returns a hash
|
28
|
+
representation of attribute values. #attribute is an alias for #[].
|
29
|
+
|
30
|
+
== Example
|
31
|
+
|
32
|
+
class Address < ValueObjectStruct.class_with_attributes(:street,:zip); end
|
33
|
+
address = Address.new(street: "123 Main", zip: 11211)
|
34
|
+
|
35
|
+
address.street = "123 Maple" # raises NoMethodError
|
36
|
+
address[:street] = "456 Elm" # raises NoMethodError
|
37
|
+
address[1] = "789 Pine" # raises NoMethodError
|
38
|
+
|
39
|
+
address == Address.new(street: "123 Main", zip: 11211) # => true
|
40
|
+
address == Address.new(street: "123 Maple", zip: 11211) # => false
|
41
|
+
|
42
|
+
set = Set[address]
|
43
|
+
set.include? Address.new(street: "123 Main", zip: 11211) # => true
|
44
|
+
set.include? Address.new(street: "123 Main", zip: 11212) # => false
|
45
|
+
|
46
|
+
== Copyright
|
47
|
+
|
48
|
+
Copyright (c) 2012 Riley Lynch, Teleological Software, LLC.
|
49
|
+
See LICENSE.txt for further details.
|
50
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
gem.name = "value_object_struct"
|
17
|
+
gem.homepage = "http://github.com/teleological/value_object_struct"
|
18
|
+
gem.license = "MIT"
|
19
|
+
gem.summary = %Q{Immutable value objects}
|
20
|
+
gem.description = %Q{Immutable value objects using Ruby Structs}
|
21
|
+
gem.authors = ["Riley Lynch"]
|
22
|
+
end
|
23
|
+
Jeweler::RubygemsDotOrgTasks.new
|
24
|
+
|
25
|
+
require 'rspec/core'
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
28
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
task :default => :spec
|
32
|
+
|
33
|
+
require 'rdoc/task'
|
34
|
+
Rake::RDocTask.new do |rdoc|
|
35
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
36
|
+
rdoc.main = "README.rdoc"
|
37
|
+
rdoc.rdoc_dir = 'rdoc'
|
38
|
+
rdoc.rdoc_files.include('README*')
|
39
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
40
|
+
rdoc.title = "value_object_struct #{version}"
|
41
|
+
end
|
42
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.6.0
|
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
# The ValueObjectStruct module provides the
|
3
|
+
# ValueObjectStruct.class_with_attributes method for defining immutable
|
4
|
+
# value object classes based on ruby's Struct class.
|
5
|
+
|
6
|
+
module ValueObjectStruct
|
7
|
+
|
8
|
+
# ValueObjectStruct.class_with_attributes accepts a list of member
|
9
|
+
# attributes specified by Symbol and returns a Class that
|
10
|
+
# inherits from a Struct class defined with those attributes.
|
11
|
+
#
|
12
|
+
# The constructor for the resulting class accepts a Hash of
|
13
|
+
# attribute value assignments. Attributes must be specified by
|
14
|
+
# symbolic key. Undeclared attributes are ignored. Declared
|
15
|
+
# attributes of the value object which are not explicitly
|
16
|
+
# initialized are set to the default value of the initializing
|
17
|
+
# Hash (usually nil).
|
18
|
+
#
|
19
|
+
# The class also provides two factory methods: ::for_values, which
|
20
|
+
# accepts an ordered list of attribute values like the Struct class
|
21
|
+
# constructor, and ::value, which accepts a hash of attribute value
|
22
|
+
# assignments, constructs the value object, and then freezes it
|
23
|
+
# before returning it.
|
24
|
+
#
|
25
|
+
# Value object instances are Struct instances with private attribute
|
26
|
+
# write access, including both named attribute writers and Hash or
|
27
|
+
# Array style bracketed subscript assignment. If all attribute values
|
28
|
+
# are nil, the value object is considered empty.
|
29
|
+
|
30
|
+
def self.class_with_attributes(*attributes)
|
31
|
+
Struct.new(*attributes).tap do |klass|
|
32
|
+
klass.class_eval <<-"RUBY", __FILE__, __LINE__
|
33
|
+
|
34
|
+
def self.value(attributes={})
|
35
|
+
new(attributes).freeze
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(attributes={})
|
39
|
+
values = members.map { |m| attributes[m] }
|
40
|
+
super(*values)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.for_values(*values)
|
44
|
+
attributes = {}
|
45
|
+
members.each_with_index { |m,i| attributes[m] = values[i] }
|
46
|
+
new(attributes)
|
47
|
+
end
|
48
|
+
|
49
|
+
def empty?
|
50
|
+
values.all?(&:nil?)
|
51
|
+
end
|
52
|
+
|
53
|
+
def attributes
|
54
|
+
members.each_with_object({}) { |m,h| h[m.to_s] = self[m] }
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :attribute, :[]
|
58
|
+
|
59
|
+
private :[]=
|
60
|
+
|
61
|
+
private #{attributes.map {|attr| ":" + attr.to_s + "=" }.join(", ")}
|
62
|
+
|
63
|
+
RUBY
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'value_object_struct'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ValueObjectStruct do
|
5
|
+
|
6
|
+
let(:struct_class) { ValueObjectStruct.class_with_attributes(:foo,:baz) }
|
7
|
+
|
8
|
+
describe ".class_with_attributes" do
|
9
|
+
|
10
|
+
it "defines a Struct subclass" do
|
11
|
+
struct_class.superclass.should be Struct
|
12
|
+
end
|
13
|
+
|
14
|
+
it "declares the given members" do
|
15
|
+
struct_class.members.should == [:foo,:baz]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
context "as a Struct subclass" do
|
21
|
+
|
22
|
+
context "initialized with a hash" do
|
23
|
+
|
24
|
+
it "initializes members from named argument" do
|
25
|
+
instance = struct_class.new(foo: "bar", baz: "qux")
|
26
|
+
instance[:foo].should == "bar"
|
27
|
+
instance.baz.should == "qux"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "leaves members omitted from constructor hash nil" do
|
31
|
+
instance = struct_class.new(foo: "bar")
|
32
|
+
instance.baz.should be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
context "initialized without arguments" do
|
38
|
+
|
39
|
+
it "returns a null object" do
|
40
|
+
instance = struct_class.new()
|
41
|
+
instance.members.each {|m| instance[m].should be_nil}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ".for_values" do
|
47
|
+
|
48
|
+
it "initializes attributes in members order" do
|
49
|
+
instance = struct_class.for_values("bar","qux")
|
50
|
+
instance[:foo].should == "bar"
|
51
|
+
instance.baz.should == "qux"
|
52
|
+
end
|
53
|
+
|
54
|
+
context "without arguments" do
|
55
|
+
|
56
|
+
it "returns a null object" do
|
57
|
+
instance = struct_class.for_values
|
58
|
+
instance.members.each {|m| instance[m].should be_nil}
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".value" do
|
66
|
+
|
67
|
+
it "initializes members from named argument" do
|
68
|
+
instance = struct_class.value(foo: "bar", baz: "qux")
|
69
|
+
instance[:foo].should == "bar"
|
70
|
+
instance.baz.should == "qux"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns a frozen object" do
|
74
|
+
struct_class.value(foo: "bar", baz: "qux").should be_frozen
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
context "given an instance" do
|
80
|
+
|
81
|
+
let(:instance) { struct_class.new(foo: "bar") }
|
82
|
+
|
83
|
+
it "privatizes member setters" do
|
84
|
+
lambda { instance.bar = "quxx" }.
|
85
|
+
should raise_error(NoMethodError)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "privatizes hash write access" do
|
89
|
+
lambda { instance[:bar] = "quxx" }.
|
90
|
+
should raise_error(NoMethodError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "provides #attributes hash" do
|
94
|
+
instance.attributes.should == { "foo" => "bar", "baz" => nil }
|
95
|
+
end
|
96
|
+
|
97
|
+
it "provides #attribute alias" do
|
98
|
+
instance.attribute(:foo).should == "bar"
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
context "given an instance without initialized attributes" do
|
104
|
+
|
105
|
+
let(:instance) { struct_class.new() }
|
106
|
+
|
107
|
+
it "is considered empty" do
|
108
|
+
instance.should be_empty
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "value_object_struct"
|
8
|
+
s.version = "0.6.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Riley Lynch"]
|
12
|
+
s.date = "2013-03-13"
|
13
|
+
s.description = "Immutable value objects using Ruby Structs"
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE.txt",
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".rspec",
|
20
|
+
"Gemfile",
|
21
|
+
"LICENSE.txt",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"lib/value_object_struct.rb",
|
26
|
+
"spec/spec_helper.rb",
|
27
|
+
"spec/value_object_struct_spec.rb",
|
28
|
+
"value_object_struct.gemspec"
|
29
|
+
]
|
30
|
+
s.homepage = "http://github.com/teleological/value_object_struct"
|
31
|
+
s.licenses = ["MIT"]
|
32
|
+
s.require_paths = ["lib"]
|
33
|
+
s.rubygems_version = "1.8.23"
|
34
|
+
s.summary = "Immutable value objects"
|
35
|
+
|
36
|
+
if s.respond_to? :specification_version then
|
37
|
+
s.specification_version = 3
|
38
|
+
|
39
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
40
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
41
|
+
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
42
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
43
|
+
else
|
44
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
45
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
46
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
47
|
+
end
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
50
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
51
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: value_object_struct
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Riley Lynch
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
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: rdoc
|
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: jeweler
|
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
|
+
description: Immutable value objects using Ruby Structs
|
63
|
+
email:
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files:
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.rdoc
|
69
|
+
files:
|
70
|
+
- .rspec
|
71
|
+
- Gemfile
|
72
|
+
- LICENSE.txt
|
73
|
+
- README.rdoc
|
74
|
+
- Rakefile
|
75
|
+
- VERSION
|
76
|
+
- lib/value_object_struct.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
- spec/value_object_struct_spec.rb
|
79
|
+
- value_object_struct.gemspec
|
80
|
+
homepage: http://github.com/teleological/value_object_struct
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
hash: -311494150749971462
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.8.23
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: Immutable value objects
|
108
|
+
test_files: []
|