opt_struct 0.2.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.
- checksums.yaml +7 -0
- data/README.md +83 -0
- data/lib/opt_struct/class_methods.rb +63 -0
- data/lib/opt_struct/instance_methods.rb +50 -0
- data/lib/opt_struct.rb +50 -0
- data/opt_struct.gemspec +19 -0
- metadata +91 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 8ffdb6352a7c6f0ac1b8ace9d92f9618b6f15b6d
|
|
4
|
+
data.tar.gz: d87218eb3c56609fab3185facd228771ae2ac2bd
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b310d805c7e04e852828893c04591620ff55e922747b25a5551e1dc57f3bf832b401c2ad0f2a8a83bdaf39150582389a18449e9d2f9d7a08ec24bca4d05ac78d
|
|
7
|
+
data.tar.gz: 630df79a1c2ba0c2899324b69f14e2e90f72a0d366487bba94c388a63966214efa5e0fe324e72a6bacf01674a3b1fdeeffe5de264209f49de1d1bbcbedd8bb1d
|
data/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# The Opt Struct
|
|
2
|
+
|
|
3
|
+
A struct around a hash
|
|
4
|
+
|
|
5
|
+
## Example 1
|
|
6
|
+
|
|
7
|
+
Can work mostly like a regular struct, while accepting options
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
MyClass = OptStruct.new(:foo, :bar)
|
|
11
|
+
|
|
12
|
+
MyClass.new
|
|
13
|
+
# => argument error
|
|
14
|
+
|
|
15
|
+
MyClass.new "foo", "bar"
|
|
16
|
+
# => #<MyClass>
|
|
17
|
+
|
|
18
|
+
i = MyClass.new "foo", "bar", yin: "yang"
|
|
19
|
+
i.options
|
|
20
|
+
# => {yin: "yang"}
|
|
21
|
+
i.fetch(:yin)
|
|
22
|
+
# => "yang"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Example 2
|
|
26
|
+
|
|
27
|
+
Passing a hash promotes the keys (`:foo` below) to an `option` giving it getter/setter methods on the class. The value becomes the default. This is equivalent and can be combined with using the `option` macro.
|
|
28
|
+
|
|
29
|
+
If an option is required it needs to be called out as such using `required`.
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
class MyClass < OptStruct.new(foo: "bar")
|
|
33
|
+
required :yin # equivalent to: `option :yin, required: true`
|
|
34
|
+
option :bar, default: "foo"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
MyClass.new
|
|
38
|
+
# => missing keyword argument :yin
|
|
39
|
+
|
|
40
|
+
i = MyClass.new yin: "yang"
|
|
41
|
+
# => #<MyClass>
|
|
42
|
+
i.foo
|
|
43
|
+
# => "bar"
|
|
44
|
+
i.bar
|
|
45
|
+
# => "foo"
|
|
46
|
+
i.foo = "foo"
|
|
47
|
+
i.options
|
|
48
|
+
# => {foo: "foo", bar: "foo", yin: "yang"}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Example 3
|
|
52
|
+
|
|
53
|
+
Works as a plain old mixin as well.
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
class MyClass
|
|
57
|
+
include OptStruct
|
|
58
|
+
required :foo
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
MyClass.new
|
|
62
|
+
# => missing keyword argument :foo
|
|
63
|
+
|
|
64
|
+
MyClass.new(foo: "bar").foo
|
|
65
|
+
# => "bar"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Example 4
|
|
69
|
+
|
|
70
|
+
Options passed to `new` can be passed to `build` when used in module form.
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
class MyClass
|
|
74
|
+
include OptStruct.build(:foo, bar: nil)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
MyClass.new
|
|
78
|
+
# => argument error
|
|
79
|
+
|
|
80
|
+
i = MyClass.new("something", bar: "foo")
|
|
81
|
+
[i.foo, i.bar]
|
|
82
|
+
# => ["something", "foo"]
|
|
83
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module OptStruct
|
|
2
|
+
module ClassMethods
|
|
3
|
+
def inherited(subclass)
|
|
4
|
+
instance_variables.each do |v|
|
|
5
|
+
subclass.send(:instance_variable_set, v, instance_variable_get(v).dup)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def required_keys
|
|
10
|
+
@required_keys ||= []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def required(*keys)
|
|
14
|
+
required_keys.concat keys
|
|
15
|
+
option_accessor *keys
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def option_reader(*keys)
|
|
19
|
+
keys.each do |key|
|
|
20
|
+
define_method(key) { options[key] }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def option_writer(*keys)
|
|
25
|
+
keys.each do |key|
|
|
26
|
+
define_method("#{key}=") { |value| options[key] = value }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def option_accessor(*keys)
|
|
31
|
+
option_reader *keys
|
|
32
|
+
option_writer *keys
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def option(key, default = nil, **options)
|
|
36
|
+
default = options[:default] if options.key?(:default)
|
|
37
|
+
defaults[key] = default
|
|
38
|
+
required_keys << key if options[:required]
|
|
39
|
+
option_accessor key
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def options(*keys, **keys_defaults)
|
|
43
|
+
option_accessor *keys if keys.any?
|
|
44
|
+
if keys_defaults.any?
|
|
45
|
+
defaults.merge!(keys_defaults)
|
|
46
|
+
option_accessor *(keys_defaults.keys - expected_arguments)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def defaults
|
|
51
|
+
@defaults ||= {}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def expect_arguments(*arguments)
|
|
55
|
+
@expected_arguments = arguments
|
|
56
|
+
attr_accessor *arguments
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def expected_arguments
|
|
60
|
+
@expected_arguments || []
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module OptStruct
|
|
2
|
+
module InstanceMethods
|
|
3
|
+
def initialize(*values, **options)
|
|
4
|
+
@options = self.class.defaults.merge(options)
|
|
5
|
+
steal_arguments_from_options
|
|
6
|
+
assign_arguments(values)
|
|
7
|
+
check_arguments(values)
|
|
8
|
+
check_required_keys
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def fetch(*a, &b)
|
|
12
|
+
options.fetch(*a, &b)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def steal_arguments_from_options
|
|
18
|
+
expected_arguments.each do |arg|
|
|
19
|
+
send("#{arg}=", options.delete(arg)) if options.key?(arg)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def assign_arguments(values)
|
|
24
|
+
values.each_with_index do |value, i|
|
|
25
|
+
send("#{expected_arguments[i]}=", value)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def check_arguments(args)
|
|
30
|
+
expected = expected_arguments.count
|
|
31
|
+
actual = expected_arguments.count do |arg|
|
|
32
|
+
instance_variable_defined?("@#{arg}")
|
|
33
|
+
end
|
|
34
|
+
unless actual == expected
|
|
35
|
+
raise ArgumentError, "only #{actual} of #{expected} required arguments present"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def check_required_keys
|
|
40
|
+
missing = self.class.required_keys.select { |key| !options.key?(key) }
|
|
41
|
+
if missing.any?
|
|
42
|
+
raise ArgumentError, "missing required keywords: #{missing.inspect}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def expected_arguments
|
|
47
|
+
self.class.expected_arguments
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/opt_struct.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require "opt_struct/class_methods"
|
|
2
|
+
require "opt_struct/instance_methods"
|
|
3
|
+
|
|
4
|
+
module OptStruct
|
|
5
|
+
def self.inject_struct(target_klass, &more_block)
|
|
6
|
+
target_klass.instance_exec do
|
|
7
|
+
extend ClassMethods
|
|
8
|
+
attr_reader :options
|
|
9
|
+
include InstanceMethods
|
|
10
|
+
end
|
|
11
|
+
target_klass.instance_exec(&more_block) if block_given?
|
|
12
|
+
target_klass
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.included(klass)
|
|
16
|
+
inject_struct(klass)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.new(*args, **defaults)
|
|
20
|
+
check_for_invalid_args(args)
|
|
21
|
+
args.map!(&:to_sym)
|
|
22
|
+
inject_struct(Class.new) do
|
|
23
|
+
expect_arguments *args
|
|
24
|
+
options defaults
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.build(*args, **defaults)
|
|
29
|
+
check_for_invalid_args(args)
|
|
30
|
+
args.map!(&:to_sym)
|
|
31
|
+
Module.new do
|
|
32
|
+
@arguments = args
|
|
33
|
+
@defaults = defaults
|
|
34
|
+
|
|
35
|
+
def self.included(klass)
|
|
36
|
+
arguments, defaults = @arguments, @defaults
|
|
37
|
+
OptStruct.inject_struct(klass) do
|
|
38
|
+
expect_arguments *arguments
|
|
39
|
+
options defaults
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def self.check_for_invalid_args(args)
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
data/opt_struct.gemspec
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Gem::Specification.new do |spec|
|
|
2
|
+
spec.name = "opt_struct"
|
|
3
|
+
spec.version = "0.2.0"
|
|
4
|
+
spec.authors = ["Carl Zulauf"]
|
|
5
|
+
spec.email = ["carl@linkleaf.com"]
|
|
6
|
+
|
|
7
|
+
spec.summary = %q{The Option Struct}
|
|
8
|
+
spec.description = %q{Struct with support for keyword params and mixin support}
|
|
9
|
+
spec.homepage = "https://github.com/carlzulauf/opt_struct"
|
|
10
|
+
spec.license = "MIT"
|
|
11
|
+
|
|
12
|
+
spec.files = `git ls-files`.split("\n").grep(/^lib/)
|
|
13
|
+
spec.files += %w(README.md opt_struct.gemspec)
|
|
14
|
+
spec.require_paths = ["lib"]
|
|
15
|
+
|
|
16
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
|
17
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
18
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: opt_struct
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Carl Zulauf
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-05-28 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.14'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.14'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.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: '3.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.0'
|
|
55
|
+
description: Struct with support for keyword params and mixin support
|
|
56
|
+
email:
|
|
57
|
+
- carl@linkleaf.com
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- README.md
|
|
63
|
+
- lib/opt_struct.rb
|
|
64
|
+
- lib/opt_struct/class_methods.rb
|
|
65
|
+
- lib/opt_struct/instance_methods.rb
|
|
66
|
+
- opt_struct.gemspec
|
|
67
|
+
homepage: https://github.com/carlzulauf/opt_struct
|
|
68
|
+
licenses:
|
|
69
|
+
- MIT
|
|
70
|
+
metadata: {}
|
|
71
|
+
post_install_message:
|
|
72
|
+
rdoc_options: []
|
|
73
|
+
require_paths:
|
|
74
|
+
- lib
|
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '0'
|
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '0'
|
|
85
|
+
requirements: []
|
|
86
|
+
rubyforge_project:
|
|
87
|
+
rubygems_version: 2.6.11
|
|
88
|
+
signing_key:
|
|
89
|
+
specification_version: 4
|
|
90
|
+
summary: The Option Struct
|
|
91
|
+
test_files: []
|