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 ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+
2
+ source "http://rubygems.org"
3
+
4
+ group :development do
5
+ gem "rspec"
6
+ gem "rdoc"
7
+ gem "jeweler"
8
+ end
9
+
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
+
@@ -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: []