opt_struct 1.0.0 → 1.2.1
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 +4 -4
- data/README.md +2 -2
- data/lib/opt_struct.rb +6 -5
- data/lib/opt_struct/class_methods.rb +84 -39
- data/lib/opt_struct/instance_methods.rb +12 -13
- data/lib/opt_struct/module_methods.rb +2 -2
- data/lib/opt_struct/version.rb +1 -1
- data/opt_struct.gemspec +0 -4
- metadata +4 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef6cb2533d486038390ac0975e38f6c0d3e9486c8c213326f48caf58c57dfb09
|
4
|
+
data.tar.gz: 98c38c235c3e24588f9a3efb7d69ac4e40da9d7d6650ebb1a0aca3c669240f45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f7346d74b2ddcdef6ff61ba638e794247fbc714c9e796a0f13297fae9d77d7482f452e43f5b2830c54129b3d6cf22f19d8315d75c360d1eb575027325a33337
|
7
|
+
data.tar.gz: cbeba0f215a42989eb6124fe095d6d6332fe015b844f972ba63aa15b085b0f7df6812751c736867743f86029187b829e44fd9d8aed88d78e8087076e78192e06
|
data/README.md
CHANGED
@@ -108,7 +108,7 @@ class AuditLog
|
|
108
108
|
end
|
109
109
|
```
|
110
110
|
|
111
|
-
These
|
111
|
+
These examples result in an `AuditLog` class with identical behavior, but no explicit `Visitable` module.
|
112
112
|
|
113
113
|
```ruby
|
114
114
|
class AuditLog
|
@@ -153,7 +153,7 @@ end
|
|
153
153
|
Passing a Hash to `.new` or `.build` is equivalent to passing the same hash to `options`
|
154
154
|
|
155
155
|
```ruby
|
156
|
-
User
|
156
|
+
User = OptStruct.new(email: nil, role: "member")
|
157
157
|
```
|
158
158
|
|
159
159
|
Default blocks can also be used and are late evaluated on the each struct instance.
|
data/lib/opt_struct.rb
CHANGED
@@ -3,10 +3,11 @@ require "opt_struct/module_methods"
|
|
3
3
|
require "opt_struct/instance_methods"
|
4
4
|
|
5
5
|
module OptStruct
|
6
|
+
RESERVED_WORDS = %i(class defaults options fetch check_required_args check_required_keys).freeze
|
6
7
|
|
7
|
-
def self._inject_struct(target, source, args = [], defaults
|
8
|
+
def self._inject_struct(target, source, args = [], **defaults, &callback)
|
8
9
|
structs = Array(source.instance_variable_get(:@_opt_structs)).dup
|
9
|
-
if args.any? || defaults.any? ||
|
10
|
+
if args.any? || defaults.any? || block_given?
|
10
11
|
structs << [args, defaults, callback]
|
11
12
|
end
|
12
13
|
target.instance_variable_set(:@_opt_structs, structs)
|
@@ -18,7 +19,7 @@ module OptStruct
|
|
18
19
|
end
|
19
20
|
structs.each do |s_args, s_defaults, s_callback|
|
20
21
|
target.expect_arguments *s_args if s_args.any?
|
21
|
-
target.options s_defaults
|
22
|
+
target.options **s_defaults if s_defaults.any?
|
22
23
|
target.class_exec(&s_callback) if s_callback
|
23
24
|
end
|
24
25
|
else
|
@@ -38,10 +39,10 @@ module OptStruct
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def self.new(*args, **defaults, &callback)
|
41
|
-
_inject_struct(Class.new, self, args.map(&:to_sym), defaults, &callback)
|
42
|
+
_inject_struct(Class.new, self, args.map(&:to_sym), **defaults, &callback)
|
42
43
|
end
|
43
44
|
|
44
45
|
def self.build(*args, **defaults, &callback)
|
45
|
-
_inject_struct(Module.new, self, args.map(&:to_sym), defaults, &callback)
|
46
|
+
_inject_struct(Module.new, self, args.map(&:to_sym), **defaults, &callback)
|
46
47
|
end
|
47
48
|
end
|
@@ -1,74 +1,82 @@
|
|
1
1
|
module OptStruct
|
2
2
|
module ClassMethods
|
3
3
|
def inherited(subclass)
|
4
|
-
|
5
|
-
|
6
|
-
subclass.send(:instance_variable_set, v, ivar.dup) if ivar
|
4
|
+
opt_struct_class_constants.each do |c|
|
5
|
+
subclass.const_set(c, const_get(c)) if const_defined?(c)
|
7
6
|
end
|
8
7
|
end
|
9
8
|
|
9
|
+
# overwritten if `required` is called
|
10
10
|
def required_keys
|
11
|
-
|
11
|
+
[].freeze
|
12
12
|
end
|
13
13
|
|
14
|
-
def required(*keys)
|
15
|
-
|
16
|
-
option_accessor *keys
|
14
|
+
def required(*keys, **options)
|
15
|
+
add_required_keys *keys
|
16
|
+
option_accessor *keys, **options
|
17
17
|
end
|
18
18
|
|
19
|
-
def option_reader(*keys)
|
19
|
+
def option_reader(*keys, **options)
|
20
20
|
keys.each do |key|
|
21
|
-
|
22
|
-
|
23
|
-
options[key]
|
24
|
-
elsif defaults.key?(key)
|
25
|
-
options[key] = read_default_value(key)
|
21
|
+
class_eval <<~RUBY
|
22
|
+
#{options[:private] ? "private" : ""} def #{key}
|
23
|
+
options[:#{key}]
|
26
24
|
end
|
27
|
-
|
25
|
+
RUBY
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
|
-
def option_writer(*keys)
|
29
|
+
def option_writer(*keys, **options)
|
32
30
|
keys.each do |key|
|
33
|
-
|
31
|
+
class_eval <<~RUBY
|
32
|
+
#{options[:private] ? "private" : ""} def #{key}=(value)
|
33
|
+
options[:#{key}] = value
|
34
|
+
end
|
35
|
+
RUBY
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
|
-
def option_accessor(*keys)
|
39
|
+
def option_accessor(*keys, **options)
|
38
40
|
check_reserved_words(keys)
|
39
|
-
option_reader *keys
|
40
|
-
option_writer *keys
|
41
|
+
option_reader *keys, **options
|
42
|
+
option_writer *keys, **options
|
41
43
|
end
|
42
44
|
|
43
|
-
def option(key, default = nil, **options)
|
44
|
-
default
|
45
|
-
|
46
|
-
|
47
|
-
option_accessor key
|
45
|
+
def option(key, default = nil, required: false, **options)
|
46
|
+
default ||= options[:default]
|
47
|
+
add_defaults key => default if default || options.key?(:default)
|
48
|
+
add_required_keys key if required
|
49
|
+
option_accessor key, **options
|
48
50
|
end
|
49
51
|
|
50
52
|
def options(*keys, **keys_defaults)
|
51
53
|
option_accessor *keys if keys.any?
|
52
54
|
if keys_defaults.any?
|
53
|
-
|
55
|
+
add_defaults keys_defaults
|
54
56
|
option_accessor *(keys_defaults.keys - expected_arguments)
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
60
|
def defaults
|
59
|
-
|
61
|
+
const_defined?(:OPT_DEFAULTS) ? const_get(:OPT_DEFAULTS) : {}
|
62
|
+
end
|
63
|
+
|
64
|
+
# overwritten if `expect_arguments` is called
|
65
|
+
def expected_arguments
|
66
|
+
[].freeze
|
60
67
|
end
|
61
68
|
|
62
69
|
def expect_arguments(*arguments)
|
63
70
|
required(*arguments)
|
64
|
-
expected_arguments
|
71
|
+
combined = expected_arguments + arguments
|
72
|
+
class_eval <<~EVAL
|
73
|
+
def self.expected_arguments
|
74
|
+
#{combined.inspect}.freeze
|
75
|
+
end
|
76
|
+
EVAL
|
65
77
|
end
|
66
78
|
alias_method :expect_argument, :expect_arguments
|
67
79
|
|
68
|
-
def expected_arguments
|
69
|
-
@expected_arguments ||= []
|
70
|
-
end
|
71
|
-
|
72
80
|
def init(meth = nil, &blk)
|
73
81
|
add_callback(:init, meth || blk)
|
74
82
|
end
|
@@ -82,23 +90,60 @@ module OptStruct
|
|
82
90
|
add_callback(:around_init, meth || blk)
|
83
91
|
end
|
84
92
|
|
85
|
-
def
|
86
|
-
|
87
|
-
@_callbacks[name] ||= []
|
88
|
-
@_callbacks[name] << callback
|
93
|
+
def all_callbacks
|
94
|
+
const_defined?(:OPT_CALLBACKS) ? const_get(:OPT_CALLBACKS) : {}.freeze
|
89
95
|
end
|
90
96
|
|
91
|
-
def
|
92
|
-
|
97
|
+
def shareable?
|
98
|
+
const_defined?(:SHAREABLE) && const_get(:SHAREABLE)
|
99
|
+
end
|
100
|
+
|
101
|
+
def shareable!
|
102
|
+
return if shareable?
|
103
|
+
const_set(:SHAREABLE, true)
|
93
104
|
end
|
94
105
|
|
95
106
|
private
|
96
107
|
|
97
|
-
|
108
|
+
def share(value)
|
109
|
+
return value unless shareable?
|
110
|
+
defined?(Ractor) ? Ractor.make_shareable(value) : value
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_required_keys(*keys)
|
114
|
+
combined = required_keys + keys
|
115
|
+
class_eval <<~RUBY
|
116
|
+
def self.required_keys
|
117
|
+
#{combined.inspect}.freeze
|
118
|
+
end
|
119
|
+
RUBY
|
120
|
+
end
|
121
|
+
|
122
|
+
def add_defaults(defaults_to_add)
|
123
|
+
freezer = defaults.dup
|
124
|
+
defaults_to_add.each { |k, v| freezer[k] = share(v) }
|
125
|
+
remove_const(:OPT_DEFAULTS) if const_defined?(:OPT_DEFAULTS)
|
126
|
+
const_set(:OPT_DEFAULTS, freezer.freeze)
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_callback(name, callback)
|
130
|
+
if const_defined?(:OPT_CALLBACKS)
|
131
|
+
callbacks_for_name = (all_callbacks[name] || []) + [callback]
|
132
|
+
callbacks_hash = all_callbacks.merge(name => callbacks_for_name).freeze
|
133
|
+
remove_const(:OPT_CALLBACKS)
|
134
|
+
const_set(:OPT_CALLBACKS, callbacks_hash)
|
135
|
+
else
|
136
|
+
const_set(:OPT_CALLBACKS, { name => [ callback ] })
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def opt_struct_class_constants
|
141
|
+
[:OPT_DEFAULTS, :OPT_CALLBACKS]
|
142
|
+
end
|
98
143
|
|
99
144
|
def check_reserved_words(words)
|
100
145
|
Array(words).each do |word|
|
101
|
-
if RESERVED_WORDS.member?(word)
|
146
|
+
if OptStruct::RESERVED_WORDS.member?(word)
|
102
147
|
raise ArgumentError, "Use of reserved word is not permitted: #{word.inspect}"
|
103
148
|
end
|
104
149
|
end
|
@@ -4,6 +4,7 @@ module OptStruct
|
|
4
4
|
with_init_callbacks do
|
5
5
|
@options = options
|
6
6
|
assign_arguments(arguments)
|
7
|
+
assign_defaults
|
7
8
|
check_required_keys
|
8
9
|
end
|
9
10
|
end
|
@@ -24,23 +25,21 @@ module OptStruct
|
|
24
25
|
raise ArgumentError, "missing required keywords: #{missing.inspect}"
|
25
26
|
end
|
26
27
|
end
|
27
|
-
|
28
|
+
|
29
|
+
def assign_defaults
|
30
|
+
defaults.each do |key, default_value|
|
31
|
+
next if options.key?(key) # || default_value.nil?
|
32
|
+
options[key] = read_default_value(default_value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
28
36
|
def assign_arguments(args)
|
29
37
|
self.class.expected_arguments.map.with_index do |key, i|
|
30
|
-
if args.length > i
|
31
|
-
options[key] = args[i]
|
32
|
-
elsif !options.key?(key)
|
33
|
-
if defaults.key?(key)
|
34
|
-
options[key] = read_default_value(key)
|
35
|
-
else
|
36
|
-
raise ArgumentError, "missing required argument: #{key}"
|
37
|
-
end
|
38
|
-
end
|
38
|
+
options[key] = args[i] if args.length > i
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
42
|
-
def read_default_value(
|
43
|
-
default = defaults[key]
|
41
|
+
|
42
|
+
def read_default_value(default)
|
44
43
|
case default
|
45
44
|
when Proc
|
46
45
|
instance_exec(&default)
|
@@ -22,8 +22,8 @@ module OptStruct
|
|
22
22
|
options
|
23
23
|
expect_arguments
|
24
24
|
).each do |class_method|
|
25
|
-
define_method(class_method) do |*args|
|
26
|
-
@_opt_structs << [[], {}, -> { send(class_method, *args) }]
|
25
|
+
define_method(class_method) do |*args, **options|
|
26
|
+
@_opt_structs << [[], {}, -> { send(class_method, *args, **options) }]
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
data/lib/opt_struct/version.rb
CHANGED
data/opt_struct.gemspec
CHANGED
@@ -16,8 +16,4 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.files = `git ls-files`.split("\n").grep(/^lib/)
|
17
17
|
spec.files += %w(README.md opt_struct.gemspec)
|
18
18
|
spec.require_paths = ["lib"]
|
19
|
-
|
20
|
-
spec.add_development_dependency "bundler", "~> 1.14"
|
21
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
23
19
|
end
|
metadata
CHANGED
@@ -1,57 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opt_struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl Zulauf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
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'
|
11
|
+
date: 2021-04-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
55
13
|
description: Struct with support for keyword params and mixin support
|
56
14
|
email:
|
57
15
|
- carl@linkleaf.com
|
@@ -85,7 +43,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
43
|
- !ruby/object:Gem::Version
|
86
44
|
version: '0'
|
87
45
|
requirements: []
|
88
|
-
rubygems_version: 3.
|
46
|
+
rubygems_version: 3.2.3
|
89
47
|
signing_key:
|
90
48
|
specification_version: 4
|
91
49
|
summary: The Option Struct
|