opt_struct 1.0.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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