opt_struct 0.2.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: 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: []