immutable-struct 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 89bdf872c24c20ae1e37d7401e9ba4a5c4c464bb
4
+ data.tar.gz: 521fa49dfeed67bba17e67df389ad5f228ad7b71
5
+ SHA512:
6
+ metadata.gz: 2cec1d2b58020662208393fffbdad46b0082a1007edeab7478e69c67ec764b7c3a1231898c4314032d748bfe0767fb1787c7f2c4e1465e73e24c3b0a40e65e13
7
+ data.tar.gz: c7251a13f171e5418d84e4aab6d36c35a18ce9d35a4c19788b5dfea888a5360657728de99999fde97c7dff624e3f17d12505480510396c4a25a1f12b0cb4959b
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ html
19
+ .*.sw?
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ immutable-struct
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - rbx-2
7
+ - ruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in immutable-struct.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dave Copeland
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,47 @@
1
+ = ImmutableStruct
2
+
3
+ {<img src="https://travis-ci.org/stitchfix/immutable-struct.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/stitchfix/immutable-struct]
4
+
5
+ Creates struct-like classes (that can build value objects) that do not have setters and also have better constructors than Ruby's built-in +Struct+.
6
+
7
+ This is highly useful for creating presenters, non-database-related models, or other quick and dirty classes in your application. Instead of using a +Hash+ or +OpenStruct+, you can create a bit more clarity around your types by using +ImmutableStruct+, which is almost as convienient.
8
+
9
+ == Install
10
+
11
+ Add to your +Gemfile+:
12
+
13
+ gem 'immutable-struct'
14
+
15
+ Then install:
16
+
17
+ bundle install
18
+
19
+ If not using bundler, just use RubyGems:
20
+
21
+ gem install immutable-struct
22
+
23
+
24
+ == To use
25
+
26
+ Person = StitchFix::ImmutableStruct.new(:name, :age, :job, :active?) do
27
+ def minor?
28
+ age < 18
29
+ end
30
+ end
31
+
32
+ p = Person.new(name: "Dave", # name will be 'Dave'
33
+ age: 40, # age will be 40
34
+ # job is omitted, so will be nil
35
+ active: true) # active and active? will be true
36
+ p.name # => "Dave"
37
+ p.age # => 40
38
+ p.active? # => true
39
+ p.minor? # => false
40
+
41
+ You can also treat the interior as a normal class definition.
42
+
43
+ == Links
44
+
45
+ * rdoc[http://stitchfix.github.io/immutable-struct]
46
+ * source[http://github.com/stitchfix/immutable-struct]
47
+ * blog[http://technology.stitchfix.com/blog/2013/12/20/presenters-delegation-vs-structs/]
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ require "bundler/gem_tasks"
2
+ require "fileutils"
3
+
4
+ include FileUtils
5
+
6
+ require "rspec/core/rake_task"
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ require "rdoc/task"
10
+ RDoc::Task.new do |rdoc|
11
+ rdoc.main = "README.rdoc"
12
+ rdoc.rdoc_files.include("README.rdoc", "lib/*.rb")
13
+ end
14
+
15
+ def sh!(command)
16
+ sh command do |ok,res|
17
+ fail "Problem running '#{command}'" unless ok
18
+ end
19
+ end
20
+
21
+ task :publish_rdoc do
22
+ rm_rf "tmp"
23
+ mkdir_p "tmp"
24
+ chdir "tmp" do
25
+ sh! "git clone git@github.com:stitchfix/immutable-struct.git"
26
+ chdir "immutable-struct" do
27
+ sh! "git checkout gh-pages"
28
+ `rm -rf *`
29
+ end
30
+ end
31
+ Rake::Task["rdoc"].invoke
32
+ `cp -R html/* tmp/immutable-struct`
33
+ chdir "tmp/immutable-struct" do
34
+ sh! "git add -A ."
35
+ sh! "git commit -m 'updated rdoc'"
36
+ sh! "git push origin gh-pages"
37
+ end
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'immutable-struct'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "immutable-struct"
8
+ spec.version = ImmutableStruct::VERSION
9
+ spec.authors = ["Stitch Fix Engineering"]
10
+ spec.email = ["jobs@stitchfix.com"]
11
+ spec.description = %q{Easily create value objects without the pain of Ruby's Struct (or its setters)}
12
+ spec.summary = %q{Easily create value objects without the pain of Ruby's Struct (or its setters)}
13
+ spec.homepage = "http://technology.stitchfix.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,67 @@
1
+ # Creates classes for value objects/read-only records. Most useful
2
+ # when creating model objects for concepts not stored in the database.
3
+ #
4
+ # This will create a class that has attr_readers for all given attributes, as
5
+ # well as a hash-based constructor. Further, the block given to with_attributes
6
+ # will be evaluated as if it were inside a class definition, allowing you
7
+ # to add methods, include or extend modules, or do whatever else you want.
8
+ class ImmutableStruct
9
+ VERSION='2.0.0' #:nodoc:
10
+ # Create a new class with the given read-only attributes.
11
+ #
12
+ # attributes:: list of symbols or strings that can be used to create attributes.
13
+ # Any attribute with a question mark in it (e.g. +:foo?+) will create
14
+ # an attribute without a question mark that passes through the raw
15
+ # value and an attribute *with* the question mark that coerces that
16
+ # value to a boolean. You would initialize it with the non-question-mark value
17
+ # block:: if present, evaluates in the context of the new class, so +def+, +def.self+, +include+
18
+ # and +extend+ should all work as in a normal class definition.
19
+ #
20
+ # Example:
21
+ #
22
+ # Person = ImmutableStruct.new(:name, :location, :minor?)
23
+ #
24
+ # p = Person.new(name: 'Dave', location: Location.new("DC"), minor: false)
25
+ # p.name # => 'Dave'
26
+ # p.location # => <Location: @where="DC">
27
+ # p.minor # => false
28
+ # p.minor? # => false
29
+ #
30
+ # p = Person.new(name: 'Rudy', minor: "yup")
31
+ # p.name # => 'Rudy'
32
+ # p.location # => nil
33
+ # p.minor # => "yup"
34
+ # p.minor? # => true
35
+ #
36
+ def self.new(*attributes,&block)
37
+ klass = Class.new do
38
+ attributes.each do |attribute|
39
+ if attribute.to_s =~ /(^.*)\?$/
40
+ raw_name = $1
41
+ attr_reader raw_name
42
+ define_method(attribute) do
43
+ !!instance_variable_get("@#{raw_name}")
44
+ end
45
+ else
46
+ attr_reader attribute
47
+ end
48
+ end
49
+
50
+ define_method(:initialize) do |*args|
51
+ attrs = args[0] || {}
52
+ attributes.each do |attribute|
53
+ ivar_name = attribute.to_s.gsub(/\?$/,'')
54
+ instance_variable_set("@#{ivar_name}",attrs[ivar_name.to_s] || attrs[ivar_name.to_sym])
55
+ end
56
+ end
57
+ end
58
+ klass.class_exec(&block) unless block.nil?
59
+ imethods = klass.instance_methods(include_super=false)
60
+ klass.class_exec(imethods) do |imethods|
61
+ define_method(:to_h) do
62
+ imethods.inject({}){ |hash, method| hash.merge(method.to_sym => self.send(method)) }
63
+ end
64
+ end
65
+ klass
66
+ end
67
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper.rb'
2
+
3
+ module TestModule
4
+ def hello; "hello"; end
5
+ end
6
+
7
+ describe ImmutableStruct do
8
+ describe "construction" do
9
+ context "with non-boolean attributes and no body" do
10
+ before do
11
+ @klass = ImmutableStruct.new(:foo, :bar, :baz)
12
+ end
13
+ subject { @klass.new }
14
+
15
+ it { should respond_to(:foo) }
16
+ it { should respond_to(:bar) }
17
+ it { should respond_to(:baz) }
18
+ it { should_not respond_to(:foo=) }
19
+ it { should_not respond_to(:bar=) }
20
+ it { should_not respond_to(:baz=) }
21
+ it { should_not respond_to(:foo?) }
22
+ it { should_not respond_to(:bar?) }
23
+ it { should_not respond_to(:baz?) }
24
+
25
+ context "instances can be created with a hash" do
26
+ subject { @klass.new(foo: "FOO", bar: 42, baz: [:a,:b,:c]) }
27
+
28
+ it { subject.foo.should == "FOO" }
29
+ it { subject.bar.should == 42 }
30
+ it { subject.baz.should == [:a,:b,:c] }
31
+ end
32
+ end
33
+
34
+ context "intelligently handles boolean attributes" do
35
+ subject { ImmutableStruct.new(:foo?) }
36
+
37
+ context "with boolean values" do
38
+ it { subject.new(foo: false).foo?.should == false }
39
+ it { subject.new(foo: false).foo.should == false }
40
+ it { subject.new(foo: true).foo?.should == true }
41
+ it { subject.new(foo: true).foo.should == true }
42
+ end
43
+
44
+ context "with falsey, non-boolean values" do
45
+ it { subject.new.foo?.should == false }
46
+ it { subject.new.foo.should == nil }
47
+ end
48
+
49
+ context "with truthy, non-boolean values" do
50
+ it { subject.new(foo: "true").foo?.should == true }
51
+ it { subject.new(foo: "true").foo.should == "true" }
52
+ end
53
+
54
+ end
55
+
56
+ it "allows defining instance methods" do
57
+ klass = ImmutableStruct.new(:foo, :bar) do
58
+ def derived; self.foo + ":" + self.bar; end
59
+ end
60
+ instance = klass.new(foo: "hello", bar: "world")
61
+ instance.derived.should == "hello:world"
62
+ end
63
+
64
+ it "allows defining class methods" do
65
+ klass = ImmutableStruct.new(:foo, :bar) do
66
+ def self.from_array(array)
67
+ self.new(foo: array[0], bar: array[1])
68
+ end
69
+ end
70
+ instance = klass.from_array(["hello","world"])
71
+ instance.foo.should == "hello"
72
+ instance.bar.should == "world"
73
+ end
74
+
75
+ it "allows module inclusion" do
76
+ klass = ImmutableStruct.new(:foo) do
77
+ include TestModule
78
+ end
79
+ instance = klass.new
80
+
81
+ instance.should respond_to(:hello)
82
+ klass.should_not respond_to(:hello)
83
+ end
84
+
85
+ it "allows module extension" do
86
+ klass = ImmutableStruct.new(:foo) do
87
+ extend TestModule
88
+ end
89
+ instance = klass.new
90
+
91
+ instance.should_not respond_to(:hello)
92
+ klass.should respond_to(:hello)
93
+ end
94
+ end
95
+
96
+ describe "to_h" do
97
+ it "should include the output of params and block methods in the hash" do
98
+ klass = ImmutableStruct.new(:flappy) do
99
+ def lawsuit
100
+ 'pending'
101
+ end
102
+ end
103
+ instance = klass.new(flappy: 'bird')
104
+ instance.to_h.should == {flappy: 'bird', lawsuit: 'pending'}
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,11 @@
1
+ GEM_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'..'))
2
+ Dir["#{GEM_ROOT}/spec/support/**/*.rb"].sort.each {|f| require f}
3
+
4
+ require 'immutable-struct'
5
+
6
+ RSpec.configure do |config|
7
+ config.expect_with :rspec do |c|
8
+ c.syntax = [:should, :expect]
9
+ end
10
+ config.order = "random"
11
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: immutable-struct
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Stitch Fix Engineering
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Easily create value objects without the pain of Ruby's Struct (or its
56
+ setters)
57
+ email:
58
+ - jobs@stitchfix.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".ruby-gemset"
65
+ - ".ruby-version"
66
+ - ".travis.yml"
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.rdoc
70
+ - Rakefile
71
+ - immutable-struct.gemspec
72
+ - lib/immutable-struct.rb
73
+ - spec/immutable_struct_spec.rb
74
+ - spec/spec_helper.rb
75
+ homepage: http://technology.stitchfix.com
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.0.rc.1
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Easily create value objects without the pain of Ruby's Struct (or its setters)
99
+ test_files:
100
+ - spec/immutable_struct_spec.rb
101
+ - spec/spec_helper.rb