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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a55fe71d013b67b97989363367748f78f0cf50ba890aeafdd69183b11c9bcac
4
- data.tar.gz: ee9465e01cac7b9978a3860cbabf0e26322246b7eda546b01a23ff6afb9715bf
3
+ metadata.gz: ef6cb2533d486038390ac0975e38f6c0d3e9486c8c213326f48caf58c57dfb09
4
+ data.tar.gz: 98c38c235c3e24588f9a3efb7d69ac4e40da9d7d6650ebb1a0aca3c669240f45
5
5
  SHA512:
6
- metadata.gz: d688e9d507c70fcc588aff5a6b9c7109eff834a7f121f0508796758b29e4c86d93c134c6e43396eca023fadc05113ca79ddae40087f20e049445c4d4343a7335
7
- data.tar.gz: 21a4f2a0a46034a18c9973596ff0f4f569d853e02f4c4b98bb3331368baee5af2b4a8718ace0a42b21c6375168cb1b6f9eabd2dff045f09f5aeed681600c69f5
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 example results in an `AuditLog` class with identical behavior, but no explicit `Visitable` module.
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 < OptStruct.new(email: nil, role: "member")
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 = {}, &callback)
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? || callback
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 if s_defaults.any?
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
- instance_variables.each do |v|
5
- ivar = instance_variable_get(v)
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
- @required_keys ||= []
11
+ [].freeze
12
12
  end
13
13
 
14
- def required(*keys)
15
- required_keys.concat keys
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
- define_method(key) do
22
- if options.key?(key)
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
- end
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
- define_method("#{key}=") { |value| options[key] = value }
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 = options[:default] if options.key?(:default)
45
- defaults[key] = default
46
- required_keys << key if options[:required]
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
- defaults.merge!(keys_defaults)
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
- @defaults ||= {}
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.concat(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 add_callback(name, callback)
86
- @_callbacks ||= {}
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 all_callbacks
92
- @_callbacks
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
- RESERVED_WORDS = %i(class defaults options fetch check_required_args check_required_keys)
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(key)
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
@@ -1,3 +1,3 @@
1
1
  module OptStruct
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.1"
3
3
  end
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.0.0
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: 2019-10-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.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.0.3
46
+ rubygems_version: 3.2.3
89
47
  signing_key:
90
48
  specification_version: 4
91
49
  summary: The Option Struct