wardrobe 0.1.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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +10 -0
- data/.gitignore +19 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +18 -0
- data/Guardfile +42 -0
- data/LICENSE +19 -0
- data/README.md +227 -0
- data/Rakefile +20 -0
- data/examples/new_york_times_article.rb +55 -0
- data/lib/wardrobe/attribute.rb +76 -0
- data/lib/wardrobe/attribute_store.rb +16 -0
- data/lib/wardrobe/block_setup.rb +124 -0
- data/lib/wardrobe/boolean.rb +6 -0
- data/lib/wardrobe/class_methods.rb +127 -0
- data/lib/wardrobe/getter_setter.rb +48 -0
- data/lib/wardrobe/instance_methods.rb +56 -0
- data/lib/wardrobe/module_methods.rb +51 -0
- data/lib/wardrobe/option.rb +30 -0
- data/lib/wardrobe/option_store.rb +22 -0
- data/lib/wardrobe/plugin.rb +39 -0
- data/lib/wardrobe/plugin_store.rb +28 -0
- data/lib/wardrobe/plugins/alias_setters.rb +35 -0
- data/lib/wardrobe/plugins/coercible/refinements/array.rb +63 -0
- data/lib/wardrobe/plugins/coercible/refinements/boolean.rb +36 -0
- data/lib/wardrobe/plugins/coercible/refinements/date.rb +31 -0
- data/lib/wardrobe/plugins/coercible/refinements/date_time.rb +29 -0
- data/lib/wardrobe/plugins/coercible/refinements/float.rb +23 -0
- data/lib/wardrobe/plugins/coercible/refinements/hash.rb +65 -0
- data/lib/wardrobe/plugins/coercible/refinements/integer.rb +23 -0
- data/lib/wardrobe/plugins/coercible/refinements/object.rb +15 -0
- data/lib/wardrobe/plugins/coercible/refinements/set.rb +33 -0
- data/lib/wardrobe/plugins/coercible/refinements/string.rb +21 -0
- data/lib/wardrobe/plugins/coercible/refinements/symbol.rb +21 -0
- data/lib/wardrobe/plugins/coercible/refinements/time.rb +30 -0
- data/lib/wardrobe/plugins/coercible/refinements/unsupported_error.rb +11 -0
- data/lib/wardrobe/plugins/coercible.rb +52 -0
- data/lib/wardrobe/plugins/configurable/configurable_store.rb +26 -0
- data/lib/wardrobe/plugins/configurable.rb +40 -0
- data/lib/wardrobe/plugins/default.rb +33 -0
- data/lib/wardrobe/plugins/dirty_tracker.rb +66 -0
- data/lib/wardrobe/plugins/equality.rb +19 -0
- data/lib/wardrobe/plugins/html_initializer.rb +38 -0
- data/lib/wardrobe/plugins/immutable.rb +164 -0
- data/lib/wardrobe/plugins/ivy_presenter.rb +42 -0
- data/lib/wardrobe/plugins/json_initializer.rb +41 -0
- data/lib/wardrobe/plugins/nil_if_empty.rb +27 -0
- data/lib/wardrobe/plugins/nil_if_zero.rb +19 -0
- data/lib/wardrobe/plugins/optional_getter.rb +21 -0
- data/lib/wardrobe/plugins/optional_setter.rb +24 -0
- data/lib/wardrobe/plugins/presenter/refinements/array.rb +15 -0
- data/lib/wardrobe/plugins/presenter/refinements/hash.rb +15 -0
- data/lib/wardrobe/plugins/presenter/refinements/object.rb +15 -0
- data/lib/wardrobe/plugins/presenter/refinements/time.rb +19 -0
- data/lib/wardrobe/plugins/presenter/refinements.rb +6 -0
- data/lib/wardrobe/plugins/presenter.rb +25 -0
- data/lib/wardrobe/plugins/validation/block_handler.rb +83 -0
- data/lib/wardrobe/plugins/validation/class_methods.rb +13 -0
- data/lib/wardrobe/plugins/validation/deep_merge.rb +27 -0
- data/lib/wardrobe/plugins/validation/error_store.rb +27 -0
- data/lib/wardrobe/plugins/validation/instance_methods.rb +39 -0
- data/lib/wardrobe/plugins/validation/refinements/_size.rb +30 -0
- data/lib/wardrobe/plugins/validation/refinements/array.rb +26 -0
- data/lib/wardrobe/plugins/validation/refinements/date.rb +15 -0
- data/lib/wardrobe/plugins/validation/refinements/hash.rb +27 -0
- data/lib/wardrobe/plugins/validation/refinements/integer.rb +21 -0
- data/lib/wardrobe/plugins/validation/refinements/nil_class.rb +19 -0
- data/lib/wardrobe/plugins/validation/refinements/numeric.rb +31 -0
- data/lib/wardrobe/plugins/validation/refinements/object.rb +35 -0
- data/lib/wardrobe/plugins/validation/refinements/set.rb +25 -0
- data/lib/wardrobe/plugins/validation/refinements/string.rb +55 -0
- data/lib/wardrobe/plugins/validation/refinements/symbol.rb +26 -0
- data/lib/wardrobe/plugins/validation/refinements.rb +13 -0
- data/lib/wardrobe/plugins/validation/validation.rb +63 -0
- data/lib/wardrobe/plugins/validation/validation_error.rb +19 -0
- data/lib/wardrobe/plugins/validation/validaton_runner.rb +38 -0
- data/lib/wardrobe/plugins/validation/validator.rb +119 -0
- data/lib/wardrobe/plugins/validation.rb +28 -0
- data/lib/wardrobe/root_config.rb +31 -0
- data/lib/wardrobe/store.rb +62 -0
- data/lib/wardrobe/stores.rb +106 -0
- data/lib/wardrobe/version.rb +5 -0
- data/lib/wardrobe.rb +34 -0
- data/sandbox/Gemfile +5 -0
- data/sandbox/Gemfile.lock +52 -0
- data/sandbox/bench_mutable_coercion.rb +91 -0
- data/sandbox/bench_test_1.rb +50 -0
- data/sandbox/bench_test_2.rb +48 -0
- data/sandbox/bench_test_3.rb +92 -0
- data/sandbox/cnn_article_find.rb +20 -0
- data/sandbox/define_method_procs.rb +139 -0
- data/sandbox/hanami_validations.rb +40 -0
- data/sandbox/middleware_bench_test.rb +97 -0
- data/sandbox/middleware_bench_v2_test.rb +130 -0
- data/test/assertions.rb +22 -0
- data/test/test_helper.rb +37 -0
- data/test/unit/atrs_config_test.rb +33 -0
- data/test/unit/atrs_root_module_test.rb +19 -0
- data/test/unit/attribute_test.rb +44 -0
- data/test/unit/block_runner/array_test.rb +29 -0
- data/test/unit/block_setup_test.rb +41 -0
- data/test/unit/class_methods_test.rb +92 -0
- data/test/unit/create_class_from_hash_test.rb +28 -0
- data/test/unit/define_getter_test.rb +94 -0
- data/test/unit/define_setter_test.rb +0 -0
- data/test/unit/disable_coercion_test.rb +19 -0
- data/test/unit/inheritance_composition_test.rb +36 -0
- data/test/unit/method_override_test.rb +18 -0
- data/test/unit/method_polution_test.rb +48 -0
- data/test/unit/option_test.rb +17 -0
- data/test/unit/plugin_test.rb +12 -0
- data/test/unit/plugins/alias_setters_test.rb +24 -0
- data/test/unit/plugins/coercible/array_test.rb +79 -0
- data/test/unit/plugins/coercible/boolean_test.rb +68 -0
- data/test/unit/plugins/coercible/custom_class_test.rb +61 -0
- data/test/unit/plugins/coercible/date_test.rb +38 -0
- data/test/unit/plugins/coercible/date_time_test.rb +44 -0
- data/test/unit/plugins/coercible/float_test.rb +42 -0
- data/test/unit/plugins/coercible/hash_test.rb +62 -0
- data/test/unit/plugins/coercible/integer_test.rb +42 -0
- data/test/unit/plugins/coercible/nested_wardrobe_test.rb +63 -0
- data/test/unit/plugins/coercible/set_test.rb +49 -0
- data/test/unit/plugins/coercible/string_test.rb +36 -0
- data/test/unit/plugins/coercible/symbol_test.rb +30 -0
- data/test/unit/plugins/coercible/time_test.rb +43 -0
- data/test/unit/plugins/configurable_test.rb +64 -0
- data/test/unit/plugins/default_value_test.rb +78 -0
- data/test/unit/plugins/dirty_tracker_test.rb +78 -0
- data/test/unit/plugins/equality_test.rb +36 -0
- data/test/unit/plugins/html_initializer_test.rb +19 -0
- data/test/unit/plugins/immutable_test.rb +149 -0
- data/test/unit/plugins/ivy_presenter_test.rb +57 -0
- data/test/unit/plugins/json_initializer_test.rb +67 -0
- data/test/unit/plugins/nil_if_empty_test.rb +39 -0
- data/test/unit/plugins/nil_if_zero_test.rb +26 -0
- data/test/unit/plugins/presenter_test.rb +68 -0
- data/test/unit/plugins/validate/inheritance_test.rb +36 -0
- data/test/unit/plugins/validate/nested_model_test.rb +40 -0
- data/test/unit/plugins/validate/predicates/each_key_each_value_test.rb +34 -0
- data/test/unit/plugins/validate/predicates/each_test.rb +33 -0
- data/test/unit/plugins/validate/predicates/empty_test.rb +37 -0
- data/test/unit/plugins/validate/predicates/even_test.rb +33 -0
- data/test/unit/plugins/validate/predicates/excluded_from_test.rb +55 -0
- data/test/unit/plugins/validate/predicates/filled_test.rb +49 -0
- data/test/unit/plugins/validate/predicates/format_test.rb +28 -0
- data/test/unit/plugins/validate/predicates/gt_test.rb +36 -0
- data/test/unit/plugins/validate/predicates/gteq_test.rb +36 -0
- data/test/unit/plugins/validate/predicates/included_in_test.rb +55 -0
- data/test/unit/plugins/validate/predicates/lt_test.rb +36 -0
- data/test/unit/plugins/validate/predicates/lteq_test.rb +36 -0
- data/test/unit/plugins/validate/predicates/max_size_test.rb +43 -0
- data/test/unit/plugins/validate/predicates/min_size_test.rb +43 -0
- data/test/unit/plugins/validate/predicates/none_test.rb +49 -0
- data/test/unit/plugins/validate/predicates/odd_test.rb +33 -0
- data/test/unit/plugins/validate/predicates/size_range_test.rb +43 -0
- data/test/unit/plugins/validate/predicates/size_test.rb +43 -0
- data/test/unit/plugins/validate/predicates/type_test.rb +77 -0
- data/test/unit/plugins/validate/predicates_test.rb +73 -0
- data/test/unit/plugins/validate/validate_bang_test.rb +30 -0
- data/test/unit/store_test.rb +15 -0
- data/test/unit/stores_test.rb +18 -0
- data/wardrobe.gemspec +26 -0
- metadata +344 -0
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Wardrobe
|
6
|
+
module ClassMethods
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@wardrobe_stores, :attribute_store, :plugin_store, :option_store
|
9
|
+
|
10
|
+
def self.extended(base)
|
11
|
+
wardrobe_methods = base.instance_variable_set(:@wardrobe_methods, Module.new)
|
12
|
+
base.include(wardrobe_methods)
|
13
|
+
base.instance_variable_set(:@wardrobe_stores, Stores.new)
|
14
|
+
end
|
15
|
+
|
16
|
+
# This is called when included in another module/class
|
17
|
+
def included(base)
|
18
|
+
base.include(Wardrobe) unless base.respond_to? :wardrobe_stores
|
19
|
+
base.merge(wardrobe_stores)
|
20
|
+
end
|
21
|
+
|
22
|
+
def inherited(child)
|
23
|
+
wardrobe_methods = child.instance_variable_set(:@wardrobe_methods, Module.new)
|
24
|
+
child.include(wardrobe_methods)
|
25
|
+
child.instance_variable_set(:@wardrobe_stores, Stores.new)
|
26
|
+
child.merge(wardrobe_stores)
|
27
|
+
child.root_config = root_config
|
28
|
+
end
|
29
|
+
|
30
|
+
def wardrobe_stores(&blk)
|
31
|
+
if block_given?
|
32
|
+
@wardrobe_stores = wardrobe_stores.update(&blk)
|
33
|
+
else
|
34
|
+
@wardrobe_stores
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def root_config=(input)
|
39
|
+
@root_config = input
|
40
|
+
end
|
41
|
+
|
42
|
+
def root_config
|
43
|
+
@root_config if instance_variable_defined?(:@root_config)
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_getters
|
47
|
+
[
|
48
|
+
Wardrobe.getters[:getter]
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_setters
|
53
|
+
[
|
54
|
+
Wardrobe.setters[:setter]
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def merge(config)
|
59
|
+
@wardrobe_stores = wardrobe_stores.merge(config, self)
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_getter(atr)
|
63
|
+
@wardrobe_methods.instance_exec do
|
64
|
+
define_method(atr.name) do
|
65
|
+
atr.getters.inject(nil) do |val, getter|
|
66
|
+
getter.block.call(val, atr, self)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def define_setter(atr)
|
73
|
+
@wardrobe_methods.instance_exec do
|
74
|
+
define_method(atr.setter_name) do |input|
|
75
|
+
atr.setters.inject(input) do |val, setter|
|
76
|
+
setter.block.call(val, atr, self)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def attribute(name, klass, *args, &blk)
|
84
|
+
merged_args = option_store.defaults.merge(args.inject({}) { |input, val| input.merge! val })
|
85
|
+
@wardrobe_stores = wardrobe_stores.add_attribute(
|
86
|
+
name, klass, self, **merged_args, &blk
|
87
|
+
)
|
88
|
+
define_getter(attribute_store[name])
|
89
|
+
define_setter(attribute_store[name])
|
90
|
+
end
|
91
|
+
|
92
|
+
def attributes(**kargs, &blk)
|
93
|
+
BlockSetup.new(self).run(**kargs, &blk)
|
94
|
+
end
|
95
|
+
|
96
|
+
def remove_attributes(*wardrobe)
|
97
|
+
wardrobe.each do |name|
|
98
|
+
@wardrobe_stores = wardrobe_stores.remove_attribute(name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
alias remove_attribute remove_attributes
|
103
|
+
|
104
|
+
def plugin(name, **args)
|
105
|
+
@wardrobe_stores = wardrobe_stores.enable_plugin(name, **args)
|
106
|
+
plugin = plugin_store[name][:klass]
|
107
|
+
|
108
|
+
if plugin.const_defined?(:ClassMethods)
|
109
|
+
extend(plugin.const_get(:ClassMethods))
|
110
|
+
end
|
111
|
+
|
112
|
+
if plugin.const_defined?(:InstanceMethods)
|
113
|
+
include(plugin.const_get(:InstanceMethods))
|
114
|
+
end
|
115
|
+
|
116
|
+
# Currently these are not needed
|
117
|
+
#
|
118
|
+
# if plugin.const_defined?(:AttributeClassMethods)
|
119
|
+
# Attribute.extend(plugin.const_get(:AttributeClassMethods))
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# if plugin.const_defined?(:AttributeInstanceMethods)
|
123
|
+
# Attribute.include(plugin.const_get(:AttributeInstanceMethods))
|
124
|
+
# end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Wardrobe
|
2
|
+
class << self
|
3
|
+
attr_reader :setters, :getters
|
4
|
+
end
|
5
|
+
@setters = {}
|
6
|
+
@getters = {}
|
7
|
+
|
8
|
+
class SetterGetter
|
9
|
+
attr_reader :name, :priority, :block, :use_if
|
10
|
+
|
11
|
+
def initialize(name, priority, block, use_if)
|
12
|
+
@name = name
|
13
|
+
@priority = priority
|
14
|
+
@block = block
|
15
|
+
@use_if = use_if
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
priority <=> other.priority
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.register_setter(name:, priority:, setter:, use_if: ->(_atr) { true })
|
24
|
+
raise 'Name taken' if setters[name]
|
25
|
+
setters[name] = SetterGetter.new(name, priority, setter, use_if)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.register_getter(name:, priority:, getter:, use_if: ->(_atr) { true })
|
29
|
+
raise 'Name taken' if getters[name]
|
30
|
+
getters[name] = SetterGetter.new(name, priority, getter, use_if)
|
31
|
+
end
|
32
|
+
|
33
|
+
Wardrobe.register_getter(
|
34
|
+
name: :getter,
|
35
|
+
priority: 0,
|
36
|
+
getter: lambda do |_value, atr, instance|
|
37
|
+
instance.instance_variable_get(atr.ivar_name)
|
38
|
+
end
|
39
|
+
)
|
40
|
+
|
41
|
+
Wardrobe.register_setter(
|
42
|
+
name: :setter,
|
43
|
+
priority: 100,
|
44
|
+
setter: lambda do |value, atr, instance|
|
45
|
+
instance.instance_variable_set(atr.ivar_name, value)
|
46
|
+
end
|
47
|
+
)
|
48
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module InstanceMethods
|
5
|
+
def initialize(**hash)
|
6
|
+
# raise "POC: More than one arg not supported..." if args.length > 1
|
7
|
+
# kargs = args.first.merge(kargs) if args.any?
|
8
|
+
_initialize { _wardrobe_init(hash) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def _initializing?
|
12
|
+
instance_variable_defined?(:@_initializing) && @_initializing
|
13
|
+
end
|
14
|
+
|
15
|
+
def _attribute_store
|
16
|
+
_attribute_store_singleton || self.class.attribute_store
|
17
|
+
end
|
18
|
+
|
19
|
+
def _add_attribute(name, klass, **args, &blk)
|
20
|
+
@_wardrobe_set_singleton = _attribute_store.add(
|
21
|
+
name, klass, self.class, self.class.wardrobe_stores, **args, &blk
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def _set_attribute_value(atr, value)
|
26
|
+
instance_variable_set(atr.ivar_name, value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def _get_attribute_value(atr)
|
30
|
+
instance_variable_get(atr.ivar_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def _initialize
|
36
|
+
instance_variable_set(:@_initializing, true)
|
37
|
+
yield
|
38
|
+
remove_instance_variable(:@_initializing)
|
39
|
+
end
|
40
|
+
|
41
|
+
def _attribute_store_singleton
|
42
|
+
@_wardrobe_set_singleton if instance_variable_defined?('@_wardrobe_set_singleton')
|
43
|
+
end
|
44
|
+
|
45
|
+
def _wardrobe_init(data)
|
46
|
+
# Should we also loop over the hash and report on missing or additional attributes?
|
47
|
+
_attribute_store.each do |name, atr|
|
48
|
+
_attribute_init(atr, data, name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def _attribute_init(atr, hash, name)
|
53
|
+
send(atr.setter_name, hash[name])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Wardrobe
|
6
|
+
# This module is extended in Wardrobe and extended to anonymous modules created by
|
7
|
+
# Wardrobe() method.
|
8
|
+
module ModuleMethods
|
9
|
+
def included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
unless base.to_s == 'Wardrobe::RootConfig'
|
12
|
+
base.root_config = config
|
13
|
+
config.default_plugins.each do |plugin|
|
14
|
+
# TODO: Support k_args
|
15
|
+
base.plugin plugin
|
16
|
+
end
|
17
|
+
end
|
18
|
+
base.include(InstanceMethods)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_class(plugins: [], attributes: [])
|
22
|
+
Class.new.class_exec do
|
23
|
+
include Wardrobe
|
24
|
+
plugin(*plugins)
|
25
|
+
attributes.each do |atr|
|
26
|
+
attribute(atr[:name], const_get(atr[:class]), atr.fetch(:options, {}))
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def config
|
33
|
+
@config ||= begin
|
34
|
+
callee = caller_locations.first
|
35
|
+
if callee.label == 'config' && callee.lineno == (__LINE__ + 3)
|
36
|
+
RootConfig.new
|
37
|
+
else
|
38
|
+
Wardrobe.config
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure(&blk)
|
44
|
+
@config = config.mutate(&blk)
|
45
|
+
end
|
46
|
+
|
47
|
+
def logger
|
48
|
+
config.logger
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
class Option
|
5
|
+
attr_reader :name, :klass, :plugin, :default, :options, :setter, :getter
|
6
|
+
|
7
|
+
def initialize(name, klass, plugin, **kargs)
|
8
|
+
@name = name
|
9
|
+
@klass = klass
|
10
|
+
@plugin = plugin
|
11
|
+
@default = kargs.fetch(:default, nil)
|
12
|
+
@getter = Wardrobe.getters[kargs.fetch(:getter, nil)]
|
13
|
+
@setter = Wardrobe.setters[kargs.fetch(:setter, nil)]
|
14
|
+
@options = kargs
|
15
|
+
freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
def klass_name
|
19
|
+
klass.to_s[/([^:]+)$/,1].downcase
|
20
|
+
end
|
21
|
+
|
22
|
+
def use_getter_for_atr?(atr)
|
23
|
+
getter&.use_if&.call(atr)
|
24
|
+
end
|
25
|
+
|
26
|
+
def use_setter_for_atr?(atr)
|
27
|
+
setter&.use_if&.call(atr)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
class OptionStore < Store
|
5
|
+
attr_reader :store, :defaults
|
6
|
+
|
7
|
+
def freeze
|
8
|
+
@defaults = select_defaults
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(name, instance)
|
13
|
+
mutate { store[name] = instance }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def select_defaults
|
19
|
+
store.reject { |_k, v| v.default.nil? }.transform_values(&:default)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
class PluginNameTakenError < StandardError; end
|
5
|
+
class PluginOptionKeywordTakenError < StandardError; end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_reader :plugins, :options
|
9
|
+
end
|
10
|
+
@plugins = {}
|
11
|
+
@options = {}
|
12
|
+
|
13
|
+
def self.register_plugin(name, mod)
|
14
|
+
raise PluginNameTakenError, "Plugin #{name} already in use" if plugins[name]
|
15
|
+
plugins[name] = mod
|
16
|
+
end
|
17
|
+
|
18
|
+
module Plugin
|
19
|
+
def options
|
20
|
+
@options ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def option(name, klass, **kargs, &blk)
|
24
|
+
raise PluginOptionKeywordTaken if Wardrobe.options[name]
|
25
|
+
option_instance = Option.new(name, klass, self, **kargs, &blk)
|
26
|
+
Wardrobe.options[name] = option_instance
|
27
|
+
BlockSetup.register_option(option_instance)
|
28
|
+
options << option_instance
|
29
|
+
#
|
30
|
+
# # TODO: Refactor this to apply only if plugin is in use
|
31
|
+
#
|
32
|
+
# raise PluginOptionKeywordTaken if Wardrobe.options[:name]
|
33
|
+
# # These needs to go somewhere else
|
34
|
+
# @option_name = name
|
35
|
+
# @option_klass = klass
|
36
|
+
# @option_default = default
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
class NoPluginRegisteredError < StandardError; end
|
5
|
+
|
6
|
+
class PluginStore < Store
|
7
|
+
attr_reader :store
|
8
|
+
def add(name, **args)
|
9
|
+
begin
|
10
|
+
plugin = Wardrobe.plugins.fetch(name)
|
11
|
+
rescue KeyError
|
12
|
+
begin
|
13
|
+
if require "wardrobe/plugins/#{name}"
|
14
|
+
retry
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
raise NoPluginRegisteredError, name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
mutate do
|
21
|
+
store[name] = {
|
22
|
+
klass: plugin,
|
23
|
+
options: args
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module AliasSetters
|
6
|
+
extend Wardrobe::Plugin
|
7
|
+
|
8
|
+
option :alias_setter, Array
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def define_setter(atr)
|
12
|
+
super
|
13
|
+
atr.options[:alias_setter]&.each do |alias_setter|
|
14
|
+
alias_method "#{alias_setter}=", atr.setter_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module InstanceMethods
|
20
|
+
private
|
21
|
+
|
22
|
+
def _attribute_init(atr, hash, name)
|
23
|
+
return super unless atr.options[:alias_setter]
|
24
|
+
return super if atr.options[:alias_setter].empty?
|
25
|
+
keys_in_hash = atr.options[:alias_setter].select { |key| hash.key?(key) }
|
26
|
+
return super unless keys_in_hash.any?
|
27
|
+
keys_in_hash.reverse.each do |key|
|
28
|
+
send("#{name}=", atr.coerce(hash[key]))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
register_plugin(:alias_setters, Plugins::AliasSetters)
|
35
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Coercible
|
6
|
+
module Refinements
|
7
|
+
refine Array.singleton_class do
|
8
|
+
def coerce(v, _atr)
|
9
|
+
case v
|
10
|
+
when self then v
|
11
|
+
when Set then v.to_a
|
12
|
+
when NilClass then []
|
13
|
+
else
|
14
|
+
raise UnsupportedError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
refine Array do
|
22
|
+
class WrongNumberOfItemsError < StandardError; end
|
23
|
+
|
24
|
+
module ArrayInstanceCoercer
|
25
|
+
def _wardrobe_init(atr, coercer: nil)
|
26
|
+
@_wardrobe_atr = atr
|
27
|
+
@_wardrobe_coercer = coercer
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def _coerce(item)
|
32
|
+
@_wardrobe_coercer.coerce(item, @_wardrobe_atr)
|
33
|
+
end
|
34
|
+
|
35
|
+
def <<(item)
|
36
|
+
super(_coerce(item))
|
37
|
+
end
|
38
|
+
|
39
|
+
def push(*items)
|
40
|
+
super(*items.map { |i| _coerce(i) })
|
41
|
+
end
|
42
|
+
|
43
|
+
def unshift(*items)
|
44
|
+
super(*items.map { |i| _coerce(i) })
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def coerce(v, atr)
|
49
|
+
res = case v
|
50
|
+
when Array
|
51
|
+
v.map! { |item| first.coerce(item, nil) }
|
52
|
+
when NilClass then []
|
53
|
+
else
|
54
|
+
raise UnsupportedError
|
55
|
+
end
|
56
|
+
res.singleton_class.include(ArrayInstanceCoercer)
|
57
|
+
res._wardrobe_init(atr, coercer: first)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Coercible
|
6
|
+
module Refinements
|
7
|
+
TRUE_STRINGS = Set.new(['1', 'yes', 'true']).freeze
|
8
|
+
FALSE_STRINGS = Set.new(['0', 'no', 'false']).freeze
|
9
|
+
refine Wardrobe::Boolean.singleton_class do
|
10
|
+
def coerce(v, _atr)
|
11
|
+
case v
|
12
|
+
when TrueClass, FalseClass then v
|
13
|
+
when Integer then integer(v)
|
14
|
+
when String then string(v)
|
15
|
+
when NilClass then nil
|
16
|
+
else
|
17
|
+
raise UnsupportedError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def integer(v)
|
22
|
+
return false if v == 0
|
23
|
+
return true if v == 1
|
24
|
+
raise UnsupportedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def string(v)
|
28
|
+
return false if FALSE_STRINGS.include?(v)
|
29
|
+
return true if TRUE_STRINGS.include?(v)
|
30
|
+
raise UnsupportedError
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
4
|
+
|
5
|
+
require 'date'
|
6
|
+
module Wardrobe
|
7
|
+
module Plugins
|
8
|
+
module Coercible
|
9
|
+
module Refinements
|
10
|
+
refine Date.singleton_class do
|
11
|
+
def coerce(v, _atr)
|
12
|
+
case v
|
13
|
+
# DateTime has to be first. `DateTime.new === Date.new # => true`
|
14
|
+
when DateTime then v.to_date
|
15
|
+
when self then v
|
16
|
+
when String then Date.parse(v)
|
17
|
+
when Time then v.to_date
|
18
|
+
when Integer then Time.at(v).to_date
|
19
|
+
when Float then Time.at(v).to_date
|
20
|
+
when NilClass then nil
|
21
|
+
else
|
22
|
+
raise UnsupportedError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
4
|
+
|
5
|
+
module Wardrobe
|
6
|
+
module Plugins
|
7
|
+
module Coercible
|
8
|
+
module Refinements
|
9
|
+
refine DateTime.singleton_class do
|
10
|
+
def coerce(v, _atr)
|
11
|
+
case v
|
12
|
+
when self then v
|
13
|
+
when String then DateTime.parse(v)
|
14
|
+
when Date then v.to_datetime
|
15
|
+
when Time then v.to_datetime
|
16
|
+
when Integer then Time.at(v).to_datetime
|
17
|
+
when Float then Time.at(v).to_datetime
|
18
|
+
when NilClass then nil
|
19
|
+
else
|
20
|
+
raise UnsupportedError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Coercible
|
6
|
+
module Refinements
|
7
|
+
refine Float.singleton_class do
|
8
|
+
def coerce(v, _atr)
|
9
|
+
case v
|
10
|
+
when self then v
|
11
|
+
when String, Integer then v.to_f
|
12
|
+
when Time then v.to_f
|
13
|
+
when Date then v.to_time.to_f
|
14
|
+
when NilClass then nil
|
15
|
+
else
|
16
|
+
raise UnsupportedError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Coercible
|
6
|
+
module Refinements
|
7
|
+
refine Hash.singleton_class do
|
8
|
+
def coerce(v, _atr)
|
9
|
+
case v
|
10
|
+
when self then v
|
11
|
+
when Array then self[*v]
|
12
|
+
when NilClass then {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
refine Hash do
|
18
|
+
def coerce(v, atr)
|
19
|
+
case v
|
20
|
+
when Hash
|
21
|
+
coerce_hash(v, atr)
|
22
|
+
when Array
|
23
|
+
coerce_hash(self.class[*v], atr)
|
24
|
+
when NilClass then {}
|
25
|
+
else
|
26
|
+
raise UnsupportedError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
HASH_SINGLETON_MODIFIER = lambda { |coercer, atr_object|
|
31
|
+
@_coercer = coercer
|
32
|
+
@_atr = atr_object
|
33
|
+
|
34
|
+
def _coercer
|
35
|
+
self.singleton_class.instance_variable_get(:@_coercer)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self._atr; @_atr; end
|
39
|
+
|
40
|
+
def _coerce(key, value)
|
41
|
+
return _coercer[0].coerce(key, nil),
|
42
|
+
_coercer[1].coerce(value, nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
def []=(key, value)
|
46
|
+
super(*_coerce(key,value))
|
47
|
+
end
|
48
|
+
}
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def coerce_hash(h, atr)
|
53
|
+
hash = h.map do |key, value|
|
54
|
+
#TODO: Since we pass around the atr object, how should this work here? Just pass nil?
|
55
|
+
[self.first[0].coerce(key, nil), self.first[1].coerce(value, nil)]
|
56
|
+
end.to_h
|
57
|
+
|
58
|
+
hash.singleton_class.class_exec(first,atr, &HASH_SINGLETON_MODIFIER)
|
59
|
+
hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|