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 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
@@ -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: []