options_hash 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 080f15a9148fb2d6250e85f0779d4bf84813e34d
4
+ data.tar.gz: 7d41b4bc07b434c3c52df25b73c1d6dc2f38bbfc
5
+ SHA512:
6
+ metadata.gz: b7f4cbdfbb969bdee95b86fb23c61b2c31f858f6d3f62e245894691feb91746bef5617c25474971eec0c7b8336f0d6a48c1ce65ad06ecc7d4ddd4f64101e1850
7
+ data.tar.gz: 8c14f9cd0777ac19bd8a43e33ef562b109a0af748ecb648a9e8a18b2cdc39bb5036d37d2889a0fd93055f0504478d4a0bde4a55b8cfed1bf24f70f27966fd597
@@ -0,0 +1,17 @@
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
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - 1.9.2
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ - rbx-2.1.1
9
+ - ruby-head
10
+ - jruby-head
11
+ - 1.8.7
12
+ - ree
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in options_hash.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jared Grippe
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.
@@ -0,0 +1,172 @@
1
+ # OptionsHash
2
+
3
+ An OptionsHash is a definition of required and optional keys in an options hash
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'options_hash'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install options_hash
18
+
19
+ ## Usage
20
+
21
+ A simple use case goes something like this:
22
+
23
+ ```ruby
24
+ require 'net/http'
25
+
26
+ def http_get options
27
+ options = OptionsHash.parse(options) do
28
+ required :host, :path
29
+ optional :port, default: 80
30
+ end
31
+ Net::HTTP.get(options.host, options.path, options.port)
32
+ end
33
+
34
+ http_get
35
+ # => ArgumentError: wrong number of arguments (0 for 1)
36
+ http_get({})
37
+ # => ArgumentError: required options: :host, :path
38
+ http_get(host: 'google.com')
39
+ # => ArgumentError: required options: :path
40
+ http_get(host: 'google.com', path: '/')
41
+ # => "<HTML><HEAD><meta ht..."
42
+ http_get(host: 'google.com', path: '/', port: 81)
43
+ # => Net::HTTPRequestTimeOut
44
+ ```
45
+
46
+ A possibly more common use case:
47
+
48
+ ```ruby
49
+ class Person
50
+ class Options < OptionsHash
51
+ required :name
52
+ optional :age, default: 31
53
+ end
54
+
55
+ def initialize options
56
+ @options = Options.parse(options)
57
+ end
58
+ attr_reader :options
59
+ end
60
+
61
+ me = Person.new({}) # => ArgumentError: required options: :name
62
+
63
+ me = Person.new(name: 'Jared Grippe')
64
+ me.options.name # => "Jared Grippe"
65
+ me.options.age # => 31
66
+ me.options.to_hash # => {:name=>"Jared Grippe", :age=>31}
67
+
68
+ me = Person.new(name: 'Jared Grippe', age: 25)
69
+ me.options.name # => "Jared Grippe"
70
+ me.options.age # => 25
71
+ ```
72
+
73
+ Advanced usage:
74
+
75
+ ```ruby
76
+ class PersonOptions < OptionsHash
77
+ required :name
78
+ optional :favorite_color
79
+ optional :species, default: :human
80
+ end
81
+
82
+ PersonOptions.options # => {
83
+ # :name=>required without default,
84
+ # :favorite_color=>optional without default,
85
+ # :species=>optional with default}
86
+
87
+ PersonOptions[:name] # => required without default
88
+ PersonOptions[:name].required? # => true
89
+ PersonOptions[:name].has_default? # => false
90
+ PersonOptions[:name].has_default_proc? # => false
91
+ PersonOptions[:name].default # => nil
92
+
93
+ PersonOptions.parse({}) # ArgumentError: required options: :name
94
+
95
+ PersonOptions.parse(name: 'Steve')
96
+ # => #<PersonOptions {:name=>"Steve", :favorite_color=>nil, :species=>:human}>
97
+
98
+ PersonOptions.parse(name: 'Steve', favorite_color: 'blue')
99
+ # => #<PersonOptions {:name=>"Steve", :favorite_color=>"blue", :species=>:human}>
100
+
101
+ PersonOptions.parse(name: 'Steve', species: :robot)
102
+ # => #<PersonOptions {:name=>"Steve", :favorite_color=>"blue", :species=>:robot}>
103
+
104
+ options = PersonOptions.parse(name: 'Steve', species: :robot)
105
+
106
+ options.name # => "Steve"
107
+ options.favorite_color # => nil
108
+ options.species # => :robot
109
+
110
+ options.given? :name # => true
111
+ options.given? :favorite_color # => false
112
+ options.given? :species # => true
113
+
114
+ options.to_hash # => {:name=>"Steve", :favorite_color=>nil, :species=>:robot}
115
+
116
+ ```
117
+
118
+
119
+
120
+
121
+ All options can take a block to determine their default value:
122
+
123
+ ```ruby
124
+ class PersonOptions < OptionsHash
125
+ required(:first_name, :last_name) { |name| name.to_sym }
126
+ optional :full_name, default: ->{ "#{first_name} #{last_name}" }
127
+ end
128
+
129
+ PersonOptions
130
+ # => PersonOptions(required: [:first_name, :last_name], optional: [:full_name])
131
+
132
+ options = PersonOptions.parse(first_name: 'Jared', last_name: 'Grippe')
133
+ # => #<PersonOptions {:first_name=>:Jared, :last_name=>:Grippe, :full_name=>"Jared Grippe"}>
134
+
135
+ options.given_options
136
+ # => {:first_name=>"Jared", :last_name=>"Grippe"}
137
+
138
+ options[:first_name] # => :Jared
139
+ options[:last_name] # => :Grippe
140
+ options[:full_name] # => "Jared Grippe"
141
+ ```
142
+
143
+ Option hashes can inherit from other option hashes
144
+
145
+ ```ruby
146
+ class AnimalOptions < OptionsHash
147
+ required :species
148
+ optional :alive, default: true
149
+ end
150
+ class DogOptions < AnimalOptions
151
+ required :name, :breed
152
+ end
153
+
154
+ AnimalOptions
155
+ # => AnimalOptions(required: [:species], optional: [:alive])
156
+
157
+ DogOptions
158
+ # => DogOptions(required: [:breed, :name, :species], optional: [:alive])
159
+
160
+ DogOptions[:alive].default # => true
161
+ ```
162
+
163
+
164
+
165
+
166
+ ## Contributing
167
+
168
+ 1. Fork it
169
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
170
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
171
+ 4. Push to the branch (`git push origin my-new-feature`)
172
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,204 @@
1
+ require "options_hash/version"
2
+
3
+ class OptionsHash
4
+
5
+ class Option
6
+ def initialize required, default=nil
7
+ @required, @default = !!required, default
8
+ end
9
+ def required?; @required; end
10
+ attr_reader :default
11
+ def has_default?
12
+ !!default
13
+ end
14
+ def has_default_proc?
15
+ default.is_a? Proc
16
+ end
17
+ def inspect
18
+ %(#{required? ? 'required' : 'optional'} #{default ? 'with' : 'without'} default)
19
+ end
20
+ alias_method :to_s, :inspect
21
+ end
22
+
23
+ def self.inherited(subclass)
24
+ subclass.send :extend, ClassMethods
25
+ subclass.send :include, InstanceMethods
26
+ subclass
27
+ end
28
+
29
+ class << self
30
+ alias_method :_new, :new
31
+ undef_method :new
32
+ private :_new
33
+ def parse options, &block
34
+ block_given? or raise ArgumentError, 'block required', caller(2)
35
+ Class.new(self, &block).parse(options)
36
+ rescue ArgumentError => error
37
+ raise ArgumentError, error.message, caller(2)
38
+ end
39
+ end
40
+
41
+ module ClassMethods
42
+
43
+ def parse options
44
+ _new options
45
+ rescue ArgumentError => error
46
+ raise ArgumentError, error.message, caller(2)
47
+ end
48
+
49
+ def options
50
+ @options ||= {}
51
+ (superclass.respond_to?(:options) ? superclass.options : {}).merge @options
52
+ end
53
+
54
+ def option? key
55
+ keys.include? key
56
+ end
57
+
58
+ def [] key
59
+ option? key or raise KeyError, "#{key} is not an option", caller(1)
60
+ options[key]
61
+ end
62
+
63
+ def keys
64
+ options.keys.to_set
65
+ end
66
+
67
+ def required_keys
68
+ options.select{|key, option| option.required? }.keys.to_set
69
+ end
70
+
71
+ def optional_keys
72
+ options.reject{|key, option| option.required? }.keys.to_set
73
+ end
74
+
75
+ def required *options, &block
76
+ default = extract_default options, &block
77
+ set_options Option.new(true, default), *options
78
+ end
79
+
80
+ def optional *options, &block
81
+ default = extract_default options, &block
82
+ set_options Option.new(false, default), *options
83
+ end
84
+
85
+ def define_attr_readers object, instance_variable_name=:@options
86
+ self.freeze
87
+ instance_variable_name = "@#{instance_variable_name.to_s.sub(/@/,'')}"
88
+ keys.each do |key|
89
+ object.send(:define_method, "#{key}" ) do
90
+ instance_variable_get(instance_variable_name)[key]
91
+ end
92
+ end
93
+ end
94
+
95
+ alias_method :_inspect, :inspect
96
+ private :_inspect
97
+
98
+ def class_name
99
+ name || "OptionsHash:#{_inspect[/^#<Class:(\w+)/,1]}"
100
+ end
101
+
102
+ def inspect
103
+ inspect = super
104
+ required_keys = self.required_keys.to_a.sort
105
+ optional_keys = self.optional_keys.to_a.sort
106
+ "#{class_name}(required: #{required_keys.inspect}, optional: #{optional_keys.inspect})"
107
+ end
108
+ alias_method :to_s, :inspect
109
+
110
+ private
111
+
112
+ def extract_default options, &block
113
+ return block if block_given?
114
+ (options.last.is_a?(Hash) ? options.pop : {})[:default]
115
+ end
116
+
117
+ def set_options definition, *options
118
+ @options ||= {}
119
+ options.each do |key|
120
+ key = key.to_sym
121
+ @options[key] = definition.dup
122
+ define_method("#{key}" ){ fetch key }
123
+ define_method("#{key}?"){ given? key }
124
+ end
125
+ end
126
+
127
+ end
128
+
129
+ module InstanceMethods
130
+ def initialize given_options
131
+ @keys = self.class.keys.freeze
132
+ @options = self.class.options.freeze
133
+ @given_options = (given_options || {}).freeze
134
+
135
+ unknown_options = @given_options.keys - keys.to_a
136
+ unknown_options.empty? or raise ArgumentError, "unknown options: #{unknown_options.sort.map(&:inspect).join(', ')}"
137
+
138
+ missing_required_options = required_keys.to_a - @given_options.keys
139
+ missing_required_options.empty? or raise ArgumentError, "required options: #{missing_required_options.sort.map(&:inspect).join(', ')}"
140
+
141
+
142
+ @values = {}
143
+ keys.to_a.sort.each{|key| send(key) }
144
+ end
145
+ attr_reader :keys, :options, :given_options
146
+
147
+ def required_keys
148
+ @options.select do |key, option|
149
+ option.required?
150
+ end.keys.to_set
151
+ end
152
+
153
+ def given? key
154
+ @given_options.key? key
155
+ end
156
+
157
+ def fetch key
158
+ key = key.to_sym
159
+ keys.include?(key) or raise KeyError, "#{key} is not an option", caller(1)
160
+ return @values[key] if @values.key? key
161
+
162
+ option = @options[key]
163
+ default_proc = option.default if option.default && option.default.is_a?(Proc)
164
+
165
+ if option.required?
166
+ value = @given_options[key]
167
+ value = instance_exec(value, &default_proc) if option.required? && default_proc
168
+ return @values[key] = value
169
+ end
170
+
171
+ return @values[key] = @given_options[key] if @given_options.key? key
172
+ return @values[key] = default_proc ? instance_exec(&default_proc) : option.default
173
+ end
174
+ alias_method :[], :fetch
175
+
176
+ def to_hash
177
+ keys.each_with_object Hash.new do |key, hash|
178
+ hash[key] = send(key)
179
+ end
180
+ end
181
+
182
+
183
+ def inspect
184
+ %(#<#{self.class.class_name} #{to_hash.inspect}>)
185
+ end
186
+ alias_method :to_s, :inspect
187
+ end
188
+
189
+ end
190
+
191
+
192
+ # class Foo
193
+
194
+ # def whatever options={}
195
+ # OptionsHash.parse(options) do
196
+ # required :name
197
+ # optional :size, default: 42
198
+ # end
199
+ # end
200
+
201
+ # end
202
+
203
+
204
+
@@ -0,0 +1,3 @@
1
+ class OptionsHash
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'options_hash/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "options_hash"
8
+ spec.version = OptionsHash::VERSION
9
+ spec.authors = ["Jared Grippe"]
10
+ spec.email = ["jared@deadlyicon.com"]
11
+ spec.description = %q{A configurable options hash definition}
12
+ spec.summary = %q{A configurable options hash definition}
13
+ spec.homepage = "https://github.com/deadlyicon/options_hash"
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
+ spec.add_development_dependency "pry-debugger"
25
+ end
@@ -0,0 +1,295 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptionsHash do
4
+ subject{ described_class }
5
+
6
+ it { should_not respond_to :new }
7
+ it { should_not respond_to :options }
8
+ it { should_not respond_to :keys }
9
+ it { should_not respond_to :required_keys }
10
+ it { should_not respond_to :optional_keys }
11
+ it { should_not respond_to :required }
12
+ it { should_not respond_to :optional }
13
+
14
+ its(:name ){ should == 'OptionsHash' }
15
+ its(:inspect){ should == 'OptionsHash' }
16
+ its(:to_s ){ should == 'OptionsHash' }
17
+
18
+ shared_examples 'an options hash' do
19
+ it { should_not respond_to :new }
20
+ it { should respond_to :options }
21
+ it { should respond_to :keys }
22
+ it { should respond_to :required_keys }
23
+ it { should respond_to :optional_keys }
24
+ it { should respond_to :required }
25
+ it { should respond_to :optional }
26
+ end
27
+
28
+ shared_examples 'an options hash instance' do
29
+ its(:inspect){ should =~ %r[#<#{subject.class.class_name} #{subject.to_hash.inspect}>] }
30
+ its(:to_s ){ should =~ %r[#<#{subject.class.class_name} #{subject.to_hash.inspect}>] }
31
+ it 'should respond to each key' do
32
+ subject.keys.each do |key|
33
+ expect(subject).to respond_to "#{key}"
34
+ expect(subject).to respond_to "#{key}?"
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ describe '.parse' do
41
+ context 'when given no arguments' do
42
+ subject{ ->{ OptionsHash.parse } }
43
+ it { should raise_error ArgumentError, 'wrong number of arguments (0 for 1)' }
44
+ end
45
+
46
+ context 'when given no block' do
47
+ subject{ ->{ OptionsHash.parse({}) } }
48
+ it { should raise_error ArgumentError, 'block required' }
49
+ end
50
+
51
+ context 'when given insufficient options' do
52
+ subject{ ->{ OptionsHash.parse({}){ required :name } } }
53
+ it { should raise_error ArgumentError, 'required options: :name' }
54
+ end
55
+
56
+ context 'when given unknown options' do
57
+ subject{ ->{ OptionsHash.parse(foo:1){ } } }
58
+ it { should raise_error ArgumentError, 'unknown options: :foo' }
59
+ end
60
+
61
+ context 'when given valid options' do
62
+ subject do
63
+ OptionsHash.parse(name: 'steve') do
64
+ required :name
65
+ optional :size, default: 42
66
+ end
67
+ end
68
+ it_behaves_like 'an options hash instance'
69
+ its(:name){ should eq 'steve' }
70
+ its(:size){ should eq 42 }
71
+ its(:to_hash){ should eq(:name=>"steve", :size=>42) }
72
+ end
73
+ end
74
+
75
+
76
+ describe 'OptionsHash(required: [:a, :b], optional: [:c, :d])' do
77
+ let :options_hash do
78
+ Class.new(OptionsHash) do
79
+ required :a
80
+ required :b do |b|
81
+ b.inspect
82
+ end
83
+ optional :c
84
+ optional :d, default: ->{ 42 }
85
+ end
86
+ end
87
+ describe '.parse' do
88
+ context '(nil)' do
89
+ subject{ ->{ options_hash.parse(nil) } }
90
+ it{ should raise_error ArgumentError, 'required options: :a, :b' }
91
+ end
92
+ context '({})' do
93
+ subject{ ->{ options_hash.parse({}) } }
94
+ it{ should raise_error ArgumentError, 'required options: :a, :b' }
95
+ end
96
+ context '(a:1)' do
97
+ subject{ ->{ options_hash.parse(a:1) } }
98
+ it{ should raise_error ArgumentError, 'required options: :b' }
99
+ end
100
+ context '(b:1)' do
101
+ subject{ ->{ options_hash.parse(b:1) } }
102
+ it{ should raise_error ArgumentError, 'required options: :a' }
103
+ end
104
+ context '(a:1, b:2)' do
105
+ subject{ options_hash.parse(a:1, b:2) }
106
+ its(:a){ should eq 1 }
107
+ its(:b){ should eq '2' }
108
+ its(:c){ should be_nil }
109
+ its(:d){ should eq 42 }
110
+ end
111
+ context '(a:1, b:2, c:3)' do
112
+ subject{ options_hash.parse(a:1, b:2, c:3) }
113
+ its(:a){ should eq 1 }
114
+ its(:b){ should eq '2' }
115
+ its(:c){ should eq 3 }
116
+ its(:d){ should eq 42 }
117
+ end
118
+ context '(a:1, b:2, c:3, d:4)' do
119
+ subject{ options_hash.parse(a:1, b:2, c:3, d:4) }
120
+ its(:a){ should eq 1 }
121
+ its(:b){ should eq '2' }
122
+ its(:c){ should eq 3 }
123
+ its(:d){ should eq 4 }
124
+ end
125
+ context '(a: nil, b: nil, c: nil, d: nil)' do
126
+ subject{ options_hash.parse(a: nil, b: nil, c: nil, d: nil) }
127
+ its(:a){ should be_nil }
128
+ its(:b){ should eq 'nil' }
129
+ its(:c){ should be_nil }
130
+ its(:d){ should be_nil }
131
+ end
132
+ end
133
+
134
+ describe '#define_attr_readers' do
135
+ it 'should freeze the options hash' do
136
+ expect(options_hash).to_not be_frozen
137
+ options_hash.define_attr_readers(Class.new)
138
+ expect(options_hash).to be_frozen
139
+ end
140
+ it "should define attr readers for each option" do
141
+ options_hash = self.options_hash
142
+ _class = Class.new
143
+ _class.send(:define_method, :initialize) do |options|
144
+ @options = options_hash.parse(options)
145
+ end
146
+ options_hash.define_attr_readers(_class)
147
+
148
+ expect(_class.new(a:1,b:2,c:3,d:4).a).to eq 1
149
+ expect(_class.new(a:1,b:2,c:3,d:4).b).to eq '2'
150
+ expect(_class.new(a:1,b:2,c:3,d:4).c).to eq 3
151
+ expect(_class.new(a:1,b:2,c:3,d:4).d).to eq 4
152
+
153
+
154
+ options_hash = self.options_hash
155
+ _class = Class.new
156
+ _class.send(:define_method, :initialize) do |options|
157
+ @swordfish = options_hash.parse(options)
158
+ end
159
+ options_hash.define_attr_readers(_class, :@swordfish)
160
+
161
+ expect(_class.new(a:1,b:2,c:3,d:4).a).to eq 1
162
+ expect(_class.new(a:1,b:2,c:3,d:4).b).to eq '2'
163
+ expect(_class.new(a:1,b:2,c:3,d:4).c).to eq 3
164
+ expect(_class.new(a:1,b:2,c:3,d:4).d).to eq 4
165
+ end
166
+ end
167
+ end
168
+
169
+
170
+ describe 'Class.new(OptionsHash)' do
171
+ subject{ Class.new(OptionsHash) }
172
+
173
+ it_behaves_like 'an options hash'
174
+
175
+ its(:name ){ should be_nil }
176
+ its(:inspect){ should =~ %r{OptionsHash:(\w+)\(required: \[\], optional: \[\]\)} }
177
+ its(:to_s ){ should =~ %r{OptionsHash:(\w+)\(required: \[\], optional: \[\]\)} }
178
+
179
+ describe '.parse({})' do
180
+ subject{ Class.new(OptionsHash).parse({}) }
181
+ it_behaves_like 'an options hash instance'
182
+ end
183
+ describe '.parse(nil)' do
184
+ subject{ Class.new(OptionsHash).parse(nil) }
185
+ it_behaves_like 'an options hash instance'
186
+ end
187
+ end
188
+
189
+
190
+
191
+ describe EmptyOptions do
192
+ subject{ EmptyOptions }
193
+
194
+ it_behaves_like 'an options hash'
195
+
196
+ its(:name ){ should == 'EmptyOptions' }
197
+ its(:inspect){ should == %{EmptyOptions(required: [], optional: [])} }
198
+ its(:to_s ){ should == %{EmptyOptions(required: [], optional: [])} }
199
+ end
200
+
201
+ describe PersonOptions do
202
+ subject{ PersonOptions }
203
+
204
+ it_behaves_like 'an options hash'
205
+
206
+ its(:keys){ should eq Set[:name, :level_of_schooling, :height, :weight, :size, :iq, :intelegence] }
207
+
208
+ describe %(PersonOptions.parse(name: 'jared', level_of_schooling: 100, iq: 240, intelegence: 2)) do
209
+ subject{ PersonOptions.parse(name: 'jared', level_of_schooling: 100, iq: 240, intelegence: 2) }
210
+
211
+ it_behaves_like 'an options hash instance'
212
+
213
+ its(:name? ){ should be_true }
214
+ its(:level_of_schooling? ){ should be_true }
215
+ its(:height? ){ should be_false }
216
+ its(:weight? ){ should be_false }
217
+ its(:size? ){ should be_false }
218
+ its(:iq? ){ should be_true }
219
+ its(:intelegence? ){ should be_true }
220
+ its(:name ){ should eq 'jared'}
221
+ its(:level_of_schooling ){ should eq 100 }
222
+ its(:height ){ should eq 2 }
223
+ its(:weight ){ should eq 2 }
224
+ its(:size ){ should eq 400 }
225
+ its(:iq ){ should eq 120.0 }
226
+ its(:intelegence ){ should eq 1.0 }
227
+
228
+ its(:keys){ should eq Set[:name, :level_of_schooling, :height, :weight, :size, :iq, :intelegence] }
229
+ its(:to_hash){ should eq(:name=>"jared", :level_of_schooling=>100, :height=>2, :weight=>2, :size=>400, :iq=>120.0, :intelegence=>1.0) }
230
+ end
231
+
232
+ describe 'argument errors' do
233
+ it "should be raised" do
234
+ expect{ subject.parse }.to raise_error ArgumentError, 'wrong number of arguments (0 for 1)'
235
+ expect{ subject.parse({}) }.to raise_error ArgumentError, 'required options: :intelegence, :iq, :level_of_schooling, :name'
236
+ expect{ subject.parse(intelegence: 1) }.to raise_error ArgumentError, 'required options: :iq, :level_of_schooling, :name'
237
+ expect{ subject.parse(intelegence: 1, iq: 1) }.to raise_error ArgumentError, 'required options: :level_of_schooling, :name'
238
+ expect{ subject.parse(intelegence: 1, iq: 1, level_of_schooling: 1) }.to raise_error ArgumentError, 'required options: :name'
239
+ expect{ subject.parse(intelegence: 1, iq: 1, level_of_schooling: 1, name: 1) }.to_not raise_error
240
+ expect{ subject.parse(b:1, a:2, name:'steve') }.to raise_error ArgumentError, 'unknown options: :a, :b'
241
+ end
242
+ end
243
+ end
244
+
245
+ describe 'Person.new' do
246
+ let(:proc){ ->{ Person.new } }
247
+ subject{ proc }
248
+ it { should raise_error ArgumentError, 'required options: :intelegence, :iq, :level_of_schooling, :name' }
249
+
250
+ describe 'ArgumentError' do
251
+ let(:error){ begin; proc.call; rescue => error; error end }
252
+ subject{ error }
253
+
254
+ describe 'backtrace[0]' do
255
+ subject{ error.backtrace[0] }
256
+ it { should eq "#{Bundler.root+'spec/support/person.rb'}:6:in `initialize'" }
257
+ end
258
+ end
259
+ end
260
+
261
+ describe 'Person.new(father:1)' do
262
+ let(:proc){ ->{ Person.new(father:1) } }
263
+ subject{ proc }
264
+ it { should raise_error ArgumentError, 'unknown options: :father' }
265
+
266
+ describe 'ArgumentError' do
267
+ let(:error){ begin; proc.call; rescue => error; error end }
268
+ subject{ error }
269
+
270
+ describe 'backtrace[0]' do
271
+ subject{ error.backtrace[0] }
272
+ it { should eq "#{Bundler.root+'spec/support/person.rb'}:6:in `initialize'" }
273
+ end
274
+ end
275
+ end
276
+
277
+ describe %[Person.new(name: 'jared', level_of_schooling: 100, iq: 240, intelegence: 2)] do
278
+ subject{ Person.new(name: 'jared', level_of_schooling: 100, iq: 240, intelegence: 2) }
279
+ it{ should respond_to :name }
280
+ it{ should respond_to :level_of_schooling }
281
+ it{ should respond_to :height }
282
+ it{ should respond_to :weight }
283
+ it{ should respond_to :size }
284
+ it{ should respond_to :iq }
285
+ it{ should respond_to :intelegence }
286
+ its(:name ){ should eq 'jared'}
287
+ its(:level_of_schooling ){ should eq 100 }
288
+ its(:height ){ should eq 2 }
289
+ its(:weight ){ should eq 2 }
290
+ its(:size ){ should eq 400 }
291
+ its(:iq ){ should eq 120.0 }
292
+ its(:intelegence ){ should eq 1.0 }
293
+ end
294
+
295
+ end
@@ -0,0 +1,24 @@
1
+ require 'options_hash'
2
+ require 'pry-debugger'
3
+
4
+ require Bundler.root + 'spec/support/empty_options.rb'
5
+ require Bundler.root + 'spec/support/person_options.rb'
6
+ require Bundler.root + 'spec/support/person.rb'
7
+
8
+ # This file was generated by the `rspec --init` command. Conventionally, all
9
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
10
+ # Require this file using `require "spec_helper"` to ensure that it is only
11
+ # loaded once.
12
+ #
13
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
14
+ RSpec.configure do |config|
15
+ config.treat_symbols_as_metadata_keys_with_true_values = true
16
+ config.run_all_when_everything_filtered = true
17
+ config.filter_run :focus
18
+
19
+ # Run specs in random order to surface order dependencies. If you find an
20
+ # order dependency and want to debug it, you can fix the order by providing
21
+ # the seed, which is printed after each run.
22
+ # --seed 1234
23
+ config.order = 'random'
24
+ end
@@ -0,0 +1,3 @@
1
+ class EmptyOptions < OptionsHash
2
+
3
+ end
@@ -0,0 +1,9 @@
1
+ class Person
2
+
3
+ PersonOptions.define_attr_readers(self)
4
+
5
+ def initialize options=nil
6
+ @options = PersonOptions.parse(options)
7
+ end
8
+
9
+ end
@@ -0,0 +1,10 @@
1
+ class PersonOptions < OptionsHash
2
+ required :name, :level_of_schooling
3
+
4
+ optional :height, :weight, default: ->{ 2 }
5
+ optional :size, &->{ (weight * height) * 100 }
6
+
7
+ required :iq, :intelegence do |given_value|
8
+ given_value.to_f * 0.50
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: options_hash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jared Grippe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-06 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
+ - !ruby/object:Gem::Dependency
56
+ name: pry-debugger
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A configurable options hash definition
70
+ email:
71
+ - jared@deadlyicon.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - .travis.yml
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/options_hash.rb
84
+ - lib/options_hash/version.rb
85
+ - options_hash.gemspec
86
+ - spec/options_hash_spec.rb
87
+ - spec/spec_helper.rb
88
+ - spec/support/empty_options.rb
89
+ - spec/support/person.rb
90
+ - spec/support/person_options.rb
91
+ homepage: https://github.com/deadlyicon/options_hash
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.1.9
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: A configurable options hash definition
115
+ test_files:
116
+ - spec/options_hash_spec.rb
117
+ - spec/spec_helper.rb
118
+ - spec/support/empty_options.rb
119
+ - spec/support/person.rb
120
+ - spec/support/person_options.rb