serega 0.11.2 → 0.14.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +163 -13
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +9 -4
  5. data/lib/serega/attribute_normalizer.rb +4 -13
  6. data/lib/serega/object_serializer.rb +11 -0
  7. data/lib/serega/plan.rb +20 -25
  8. data/lib/serega/plan_point.rb +13 -16
  9. data/lib/serega/plugins/batch/lib/loader.rb +25 -7
  10. data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +1 -9
  11. data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +69 -0
  12. data/lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb +35 -0
  13. data/lib/serega/plugins/metadata/metadata.rb +5 -0
  14. data/lib/serega/plugins/openapi/lib/modules/config.rb +23 -0
  15. data/lib/serega/plugins/openapi/lib/openapi_config.rb +101 -0
  16. data/lib/serega/plugins/openapi/openapi.rb +245 -0
  17. data/lib/serega/plugins/preloads/lib/modules/attribute.rb +28 -0
  18. data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +99 -0
  19. data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +22 -0
  20. data/lib/serega/plugins/preloads/lib/modules/config.rb +19 -0
  21. data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +41 -0
  22. data/lib/serega/plugins/preloads/lib/preload_paths.rb +46 -0
  23. data/lib/serega/plugins/preloads/lib/preloads_config.rb +62 -0
  24. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +20 -7
  25. data/lib/serega/plugins/preloads/preloads.rb +12 -210
  26. data/lib/serega/plugins/preloads/validations/check_opt_preload_path.rb +54 -15
  27. metadata +18 -7
  28. data/lib/serega/plugins/preloads/lib/main_preload_path.rb +0 -53
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module OpenAPI
6
+ #
7
+ # Config class additional/patched instance methods
8
+ #
9
+ # @see Serega::SeregaConfig
10
+ #
11
+ module ConfigInstanceMethods
12
+ #
13
+ # Returns openapi plugin config
14
+ #
15
+ # @return [Serega::SeregaPlugins::OpenAPI::OpenAPIConfig] configuration for openapi plugin
16
+ #
17
+ def openapi
18
+ @openapi ||= OpenAPIConfig.new(self.class.serializer_class, opts.fetch(:openapi))
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module OpenAPI
6
+ #
7
+ # OpenAPI plugin config
8
+ #
9
+ class OpenAPIConfig
10
+ attr_reader :serializer_class, :opts
11
+
12
+ def initialize(serializer_class, opts)
13
+ @serializer_class = serializer_class
14
+ @opts = opts
15
+ end
16
+
17
+ #
18
+ # Saves new properties
19
+ #
20
+ # @param new_properties [Hash] new properties
21
+ #
22
+ # @return [Hash] OpenAPI properties
23
+ #
24
+ def properties(new_properties = FROZEN_EMPTY_HASH)
25
+ properties = opts[:properties]
26
+ return properties if new_properties.empty?
27
+
28
+ new_properties = SeregaUtils::EnumDeepDup.call(new_properties)
29
+ symbolize_keys!(new_properties)
30
+
31
+ new_properties.each do |attribute_name, new_attribute_properties|
32
+ check_attribute_exists(attribute_name)
33
+ check_properties_is_a_hash(attribute_name, new_attribute_properties)
34
+
35
+ properties[attribute_name] = symbolize_keys!(new_attribute_properties)
36
+ end
37
+ end
38
+
39
+ #
40
+ # @return [#call] builder of `$ref` attribute
41
+ #
42
+ def ref_builder
43
+ opts[:ref_builder]
44
+ end
45
+
46
+ #
47
+ # Sets new $ref option builder
48
+ #
49
+ # @param builder [#call] Callable object that accepts serializer_class and constructs $ref option string
50
+ #
51
+ # @return Specified new builder
52
+ #
53
+ def ref_builder=(builder)
54
+ raise SeregaError, "ref_builder must respond to #call" unless builder.respond_to?(:call)
55
+ opts[:ref_builder] = builder
56
+ end
57
+
58
+ #
59
+ # @return [#call] builder of schema name
60
+ #
61
+ def schema_name_builder
62
+ opts[:schema_name_builder]
63
+ end
64
+
65
+ #
66
+ # Sets new schema_name_builder
67
+ #
68
+ # @param builder [#call] Callable object that accepts serializer_class and
69
+ # constructs schema name to use in schemas list and in $ref option
70
+ #
71
+ # @return Specified new builder
72
+ #
73
+ def schema_name_builder=(builder)
74
+ raise SeregaError, "schema_name_builder must respond to #call" unless builder.respond_to?(:call)
75
+ opts[:schema_name_builder] = builder
76
+ end
77
+
78
+ private
79
+
80
+ def check_attribute_exists(attribute_name)
81
+ return if serializer_class.attributes.key?(attribute_name)
82
+
83
+ raise SeregaError, "No attribute with name :#{attribute_name}"
84
+ end
85
+
86
+ def check_properties_is_a_hash(attribute_name, new_attribute_properties)
87
+ return if new_attribute_properties.is_a?(Hash)
88
+
89
+ raise SeregaError, "Property #{attribute_name} value must be a Hash," \
90
+ " but #{new_attribute_properties.inspect} was provided"
91
+ end
92
+
93
+ def symbolize_keys!(opts)
94
+ opts.transform_keys! do |key|
95
+ key.to_sym
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ #
5
+ # Utility class to build OpenAPI schemas
6
+ #
7
+ class OpenAPI
8
+ #
9
+ # Constructs OpenAPI schemas for multiple serializers
10
+ #
11
+ # @params serializers [Class<Serega>] Serializers tobuild schemas,
12
+ # by default it is all serializers with :openapi plugin enabled
13
+ #
14
+ # @return [Hash] Schemas hash
15
+ #
16
+ def self.schemas(serializers = self.serializers)
17
+ serializers.each_with_object({}) do |serializer_class, schemas|
18
+ schema = serializer_class.openapi_schema
19
+ schema_name = serializer_class.openapi_schema_name
20
+ schemas[schema_name] = schema
21
+ end
22
+ end
23
+
24
+ #
25
+ # Returns list of serializers with :openapi plugin
26
+ #
27
+ def self.serializers
28
+ @serializers ||= []
29
+ end
30
+ end
31
+
32
+ module SeregaPlugins
33
+ #
34
+ # Plugin :openapi
35
+ #
36
+ # Helps to build OpenAPI schemas
37
+ #
38
+ # This schemas can be easielty used with "rswag" gem by adding them to "config.swagger_docs"
39
+ # https://github.com/rswag/rswag#referenced-parameters-and-schema-definitions
40
+ #
41
+ # This plugin only adds type "object" or "array" for relationships and marks
42
+ # attributes as **required** if they have no :hide option set.
43
+ #
44
+ # OpenAPI properties will have no any "type" or other options specified by default,
45
+ # you should provide them in 'YourSerializer.openapi_properties' method.
46
+ # `openapi_properties` can be specified multiple time, in this case they wil be merged.
47
+ #
48
+ # After enabling this plugin attributes with :serializer option will have
49
+ # to have :many option set to construct "object" or "array" openapi type for relationships.
50
+ #
51
+ # OpenAPI `$ref` property will be added automatically for all relationships.
52
+ #
53
+ # Example constructing all serializers schemas:
54
+ # `Serega::OpenAPI.schemas`
55
+ #
56
+ # Example constructing specific serializers schemas:
57
+ # `Serega::OpenAPI.schemas(Serega::OpenAPI.serializers - [MyBaseSerializer])`
58
+ #
59
+ # Example constructing one serializer schema:
60
+ # `SomeSerializer.openapi_schema`
61
+ #
62
+ # @example
63
+ # class BaseSerializer < Serega
64
+ # plugin :openapi
65
+ # end
66
+ #
67
+ # class UserSerializer < BaseSerializer
68
+ # attribute :name
69
+ #
70
+ # openapi_properties(
71
+ # name: { type: :string }
72
+ # )
73
+ # end
74
+ #
75
+ # class PostSerializer < BaseSerializer
76
+ # attribute :text
77
+ # attribute :user, serializer: UserSerializer, many: false
78
+ # attribute :comments, serializer: PostSerializer, many: true, hide: true
79
+ #
80
+ # openapi_properties(
81
+ # text: { type: :string },
82
+ # user: { type: 'object' }, # `$ref` option will be added automatically when constructing schema
83
+ # comments: { type: 'array' } # `items` option with `$ref` will be added automatically when constructing schema
84
+ # )
85
+ # end
86
+ #
87
+ # puts Serega::OpenAPI.schemas
88
+ # =>
89
+ # {
90
+ # "PostSerializer" => {
91
+ # type: "object",
92
+ # properties: {
93
+ # text: {type: "string"},
94
+ # user: {:$ref => "#/components/schemas/UserSerializer"},
95
+ # comments: {type: "array", items: {:$ref => "#/components/schemas/PostSerializer"}}
96
+ # },
97
+ # required: [:text, :comments],
98
+ # additionalProperties: false
99
+ # },
100
+ # "UserSerializer" => {
101
+ # type: "object",
102
+ # properties: {
103
+ # name: {type: "string"}
104
+ # },
105
+ # required: [:name],
106
+ # additionalProperties: false
107
+ # }
108
+ # }
109
+ #
110
+ module OpenAPI
111
+ # Builder for schema name (used is schemas list). Returns serializer class name
112
+ DEFAULT_SCHEMA_NAME_BUILDER = ->(serializer_class) { serializer_class.name }
113
+
114
+ # Builder for $ref openapi property
115
+ DEFAULT_REF_BUILDER = ->(serializer_class) { "#/components/schemas/#{serializer_class.openapi_schema_name}" }
116
+
117
+ # @return [Symbol] Plugin name
118
+ def self.plugin_name
119
+ :openapi
120
+ end
121
+
122
+ # Checks requirements and loads additional plugins
123
+ #
124
+ # @param serializer_class [Class<Serega>] Current serializer class
125
+ # @param opts [Hash] loaded plugins opts
126
+ #
127
+ # @return [void]
128
+ #
129
+ def self.before_load_plugin(serializer_class, **opts)
130
+ unless serializer_class.plugin_used?(:explicit_many_option)
131
+ serializer_class.plugin :explicit_many_option
132
+ end
133
+ end
134
+
135
+ #
136
+ # Applies plugin code to specific serializer
137
+ #
138
+ # @param serializer_class [Class<Serega>] Current serializer class
139
+ # @param _opts [Hash] Loaded plugins options
140
+ #
141
+ # @return [void]
142
+ #
143
+ def self.load_plugin(serializer_class, **opts)
144
+ require_relative "./lib/modules/config"
145
+ require_relative "./lib/openapi_config"
146
+
147
+ serializer_class.extend(ClassMethods)
148
+ serializer_class::SeregaConfig.include(ConfigInstanceMethods)
149
+
150
+ config = serializer_class.config
151
+ config.opts[:openapi] = {properties: {}}
152
+ openapi_config = serializer_class.config.openapi
153
+ openapi_config.schema_name_builder = opts[:schema_name_builder] || DEFAULT_SCHEMA_NAME_BUILDER
154
+ openapi_config.ref_builder = opts[:ref_builder] || DEFAULT_REF_BUILDER
155
+ end
156
+
157
+ #
158
+ # Adds config options and runs other callbacks after plugin was loaded
159
+ #
160
+ # @param serializer_class [Class<Serega>] Current serializer class
161
+ # @param opts [Hash] loaded plugins opts
162
+ #
163
+ # @return [void]
164
+ #
165
+ def self.after_load_plugin(serializer_class, **opts)
166
+ Serega::OpenAPI.serializers << serializer_class unless serializer_class.equal?(Serega)
167
+ end
168
+
169
+ #
170
+ # Serega additional/patched class methods
171
+ #
172
+ # @see Serega
173
+ #
174
+ module ClassMethods
175
+ #
176
+ # OpenAPI schema for current serializer
177
+ #
178
+ def openapi_schema
179
+ properties = SeregaUtils::EnumDeepDup.call(openapi_properties)
180
+ required_properties = []
181
+
182
+ attributes.each do |attribute_name, attribute|
183
+ add_openapi_property(properties, attribute_name, attribute)
184
+ add_openapi_required_property(required_properties, attribute_name, attribute)
185
+ end
186
+
187
+ {
188
+ type: "object",
189
+ properties: properties,
190
+ required: required_properties,
191
+ additionalProperties: false
192
+ }
193
+ end
194
+
195
+ #
196
+ # Adds new OpenAPI properties and returns all properties
197
+ #
198
+ # @param props [Hash] Specifies new properties
199
+ #
200
+ # @return [Hash] Specified OpenAPI properties
201
+ #
202
+ def openapi_properties(props = FROZEN_EMPTY_HASH)
203
+ config.openapi.properties(props)
204
+ end
205
+
206
+ #
207
+ # Builds OpenAPI schema name using configured builder
208
+ #
209
+ # @return [String] OpenAPI schema name
210
+ #
211
+ def openapi_schema_name
212
+ config.openapi.schema_name_builder.call(self)
213
+ end
214
+
215
+ private
216
+
217
+ def inherited(subclass)
218
+ super
219
+ Serega::OpenAPI.serializers << subclass
220
+ end
221
+
222
+ def add_openapi_property(properties, attribute_name, attribute)
223
+ property = properties[attribute_name] ||= {}
224
+ return unless attribute.relation?
225
+
226
+ ref = attribute.serializer.config.openapi.ref_builder.call(attribute.serializer)
227
+
228
+ if attribute.many
229
+ property[:type] = "array"
230
+ property[:items] ||= {}
231
+ property[:items][:$ref] ||= ref
232
+ else
233
+ property[:$ref] ||= ref
234
+ end
235
+ end
236
+
237
+ def add_openapi_required_property(required_properties, attribute_name, attribute)
238
+ required_properties << attribute_name unless attribute.hide
239
+ end
240
+ end
241
+ end
242
+
243
+ register_plugin(OpenAPI.plugin_name, OpenAPI)
244
+ end
245
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Preloads
6
+ #
7
+ # Serega::SeregaAttribute additional/patched instance methods
8
+ #
9
+ # @see Serega::SeregaAttribute::AttributeInstanceMethods
10
+ #
11
+ module AttributeInstanceMethods
12
+ # @return [Hash, nil] normalized preloads of current attribute
13
+ attr_reader :preloads
14
+
15
+ # @return [Array] normalized preloads_path of current attribute
16
+ attr_reader :preloads_path
17
+
18
+ private
19
+
20
+ def set_normalized_vars(normalizer)
21
+ super
22
+ @preloads = normalizer.preloads
23
+ @preloads_path = normalizer.preloads_path
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Preloads
6
+ #
7
+ # Serega::SeregaAttributeNormalizer additional/patched instance methods
8
+ #
9
+ # @see SeregaAttributeNormalizer::AttributeNormalizerInstanceMethods
10
+ #
11
+ module AttributeNormalizerInstanceMethods
12
+ # @return [Hash,nil] normalized attribute preloads
13
+ def preloads
14
+ return @preloads if instance_variable_defined?(:@preloads)
15
+
16
+ @preloads = prepare_preloads
17
+ end
18
+
19
+ # @return [Array, nil] normalized attribute preloads path
20
+ def preloads_path
21
+ return @preloads_path if instance_variable_defined?(:@preloads_path)
22
+
23
+ @preloads_path = prepare_preloads_path
24
+ end
25
+
26
+ private
27
+
28
+ #
29
+ # Patched in:
30
+ # - plugin :batch (extension :preloads - skips auto preloads when batch option provided)
31
+ #
32
+ def prepare_preloads
33
+ opts = init_opts
34
+ preloads_provided = opts.key?(:preload)
35
+ preloads =
36
+ if preloads_provided
37
+ opts[:preload]
38
+ elsif opts.key?(:serializer) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_serializer
39
+ key
40
+ elsif opts.key?(:delegate) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_delegate
41
+ opts[:delegate].fetch(:to)
42
+ end
43
+
44
+ # Nil and empty hash differs as we can preload nested results to
45
+ # empty hash, but we will skip nested preloading if nil or false provided
46
+ return if preloads_provided && !preloads
47
+
48
+ FormatUserPreloads.call(preloads)
49
+ end
50
+
51
+ def prepare_preloads_path
52
+ path = init_opts.fetch(:preload_path) { default_preload_path(preloads) }
53
+
54
+ if path && path[0].is_a?(Array)
55
+ prepare_many_preload_paths(path)
56
+ else
57
+ prepare_one_preload_path(path)
58
+ end
59
+ end
60
+
61
+ def prepare_one_preload_path(path)
62
+ return unless path
63
+
64
+ case path
65
+ when Array
66
+ path.map(&:to_sym).freeze
67
+ else
68
+ [path.to_sym].freeze
69
+ end
70
+ end
71
+
72
+ def prepare_many_preload_paths(paths)
73
+ paths.map { |path| prepare_one_preload_path(path) }.freeze
74
+ end
75
+
76
+ def default_preload_path(preloads)
77
+ return FROZEN_EMPTY_ARRAY if !preloads || preloads.empty?
78
+
79
+ [preloads.keys.first]
80
+ end
81
+
82
+ #
83
+ # Patch for original `prepare_hide` method
84
+ # @see
85
+ #
86
+ # Marks attribute hidden if auto_hide_attribute_with_preloads option was set and attribute has preloads
87
+ #
88
+ def prepare_hide
89
+ res = super
90
+ return res unless res.nil?
91
+
92
+ if preloads && !preloads.empty?
93
+ self.class.serializer_class.config.preloads.auto_hide_attributes_with_preload || nil
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Preloads
6
+ #
7
+ # Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
8
+ #
9
+ # @see Serega::SeregaValidations::CheckAttributeParams
10
+ #
11
+ module CheckAttributeParamsInstanceMethods
12
+ private
13
+
14
+ def check_opts
15
+ super
16
+ CheckOptPreload.call(opts)
17
+ CheckOptPreloadPath.call(opts)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Preloads
6
+ #
7
+ # Config class additional/patched instance methods
8
+ #
9
+ # @see Serega::SeregaConfig
10
+ #
11
+ module ConfigInstanceMethods
12
+ # @return [Serega::SeregaPlugins::Preloads::PreloadsConfig] `preloads` plugin config
13
+ def preloads
14
+ @preloads ||= PreloadsConfig.new(opts.fetch(:preloads))
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Preloads
6
+ #
7
+ # Serega::SeregaPlanPoint additional/patched instance methods
8
+ #
9
+ # @see Serega::SeregaPlanPoint::InstanceMethods
10
+ #
11
+ module PlanPointInstanceMethods
12
+ #
13
+ # @return [Hash] preloads for nested attributes
14
+ #
15
+ attr_reader :preloads
16
+
17
+ #
18
+ # @return [Array<Symbol>] preloads path for current attribute
19
+ #
20
+ attr_reader :preloads_path
21
+
22
+ private
23
+
24
+ def set_normalized_vars
25
+ super
26
+
27
+ @preloads = prepare_preloads
28
+ @preloads_path = prepare_preloads_path
29
+ end
30
+
31
+ def prepare_preloads
32
+ PreloadsConstructor.call(child_plan)
33
+ end
34
+
35
+ def prepare_preloads_path
36
+ attribute.preloads_path
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Preloads
6
+ #
7
+ # Utility that helps to transform preloads to array of paths
8
+ #
9
+ # Example:
10
+ #
11
+ # call({ a: { b: { c: {}, d: {} } }, e: {} })
12
+ #
13
+ # => [
14
+ # [:a],
15
+ # [:a, :b],
16
+ # [:a, :b, :c],
17
+ # [:a, :b, :d],
18
+ # [:e]
19
+ # ]
20
+ class PreloadPaths
21
+ class << self
22
+ #
23
+ # Transforms user provided preloads to array of paths
24
+ #
25
+ # @param value [Array,Hash,String,Symbol,nil,false] preloads
26
+ #
27
+ # @return [Hash] preloads transformed to hash
28
+ #
29
+ def call(preloads, path = [], result = [])
30
+ preloads = FormatUserPreloads.call(preloads)
31
+
32
+ preloads.each do |key, nested_preloads|
33
+ path << key
34
+ result << path.dup
35
+
36
+ call(nested_preloads, path, result)
37
+ path.pop
38
+ end
39
+
40
+ result
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Serega
4
+ module SeregaPlugins
5
+ module Preloads
6
+ #
7
+ # Config for `preloads` plugin
8
+ #
9
+ class PreloadsConfig
10
+ # @return [Hash] preloads plugin options
11
+ attr_reader :opts
12
+
13
+ #
14
+ # Initializes PreloadsConfig object
15
+ #
16
+ # @param opts [Hash] options
17
+ #
18
+ # @return [Serega::SeregaPlugins::Preloads::PreloadsConfig]
19
+ #
20
+ def initialize(opts)
21
+ @opts = opts
22
+ end
23
+
24
+ # @!method auto_preload_attributes_with_delegate
25
+ # @return [Boolean, nil] option value
26
+ #
27
+ # @!method auto_preload_attributes_with_delegate=(value)
28
+ # @param value [Boolean] New option value
29
+ # @return [Boolean] New option value
30
+ #
31
+ # @!method auto_preload_attributes_with_serializer
32
+ # @return [Boolean, nil] option value
33
+ #
34
+ # @!method auto_preload_attributes_with_serializer=(value)
35
+ # @param value [Boolean] New option value
36
+ # @return [Boolean] New option value
37
+ #
38
+ # @!method auto_hide_attributes_with_preload
39
+ # @return [Boolean, nil] option value
40
+ #
41
+ # @!method auto_hide_attributes_with_preload=(value)
42
+ # @param value [Boolean] New option value
43
+ # @return [Boolean] New option value
44
+ #
45
+ %i[
46
+ auto_preload_attributes_with_delegate
47
+ auto_preload_attributes_with_serializer
48
+ auto_hide_attributes_with_preload
49
+ ].each do |method_name|
50
+ define_method(method_name) do
51
+ opts.fetch(method_name)
52
+ end
53
+
54
+ define_method("#{method_name}=") do |value|
55
+ raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
56
+ opts[method_name] = value
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end