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,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Coercible
|
6
|
+
module Refinements
|
7
|
+
refine Integer.singleton_class do
|
8
|
+
def coerce(v, _atr)
|
9
|
+
case v
|
10
|
+
when self then v
|
11
|
+
when String, Float then v.to_i
|
12
|
+
when Time then v.to_i
|
13
|
+
when Date then v.to_time.to_i
|
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,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
module Wardrobe
|
5
|
+
module Plugins
|
6
|
+
module Coercible
|
7
|
+
module Refinements
|
8
|
+
refine Set.singleton_class do
|
9
|
+
def coerce(v, _atr)
|
10
|
+
case v
|
11
|
+
when self then v
|
12
|
+
when Array then v.to_set
|
13
|
+
when NilClass then new
|
14
|
+
else
|
15
|
+
raise UnsupportedError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
refine Set do
|
20
|
+
def coerce(v, _atr)
|
21
|
+
case v
|
22
|
+
when NilClass then self.class.new
|
23
|
+
when Array, Set
|
24
|
+
v.to_set.map! { |i| first.coerce(i, nil) }
|
25
|
+
else
|
26
|
+
raise UnsupportedError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Coercible
|
6
|
+
module Refinements
|
7
|
+
refine String.singleton_class do
|
8
|
+
def coerce(v, _atr)
|
9
|
+
case v
|
10
|
+
when self then v
|
11
|
+
when Integer, Float, Symbol then v.to_s
|
12
|
+
when NilClass then nil
|
13
|
+
else
|
14
|
+
raise UnsupportedError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Coercible
|
6
|
+
module Refinements
|
7
|
+
refine Symbol.singleton_class do
|
8
|
+
def coerce(v, _atr)
|
9
|
+
case v
|
10
|
+
when self then v
|
11
|
+
when String then v.to_sym
|
12
|
+
when NilClass then nil
|
13
|
+
else
|
14
|
+
raise UnsupportedError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
4
|
+
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module Wardrobe
|
8
|
+
module Plugins
|
9
|
+
module Coercible
|
10
|
+
module Refinements
|
11
|
+
refine Time.singleton_class do
|
12
|
+
def coerce(v, _atr)
|
13
|
+
case v
|
14
|
+
when self then v
|
15
|
+
when String then Time.parse(v)
|
16
|
+
when Integer then Time.at(v)
|
17
|
+
when Float then Time.at(v)
|
18
|
+
when Date then v.to_time
|
19
|
+
when NilClass then nil
|
20
|
+
else
|
21
|
+
raise UnsupportedError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'wardrobe/plugins/coercible/refinements/unsupported_error'
|
3
|
+
require 'wardrobe/plugins/coercible/refinements/array'
|
4
|
+
require 'wardrobe/plugins/coercible/refinements/set'
|
5
|
+
require 'wardrobe/plugins/coercible/refinements/date'
|
6
|
+
require 'wardrobe/plugins/coercible/refinements/date_time'
|
7
|
+
require 'wardrobe/plugins/coercible/refinements/float'
|
8
|
+
require 'wardrobe/plugins/coercible/refinements/hash'
|
9
|
+
require 'wardrobe/plugins/coercible/refinements/integer'
|
10
|
+
require 'wardrobe/plugins/coercible/refinements/object'
|
11
|
+
require 'wardrobe/plugins/coercible/refinements/string'
|
12
|
+
require 'wardrobe/plugins/coercible/refinements/boolean'
|
13
|
+
require 'wardrobe/plugins/coercible/refinements/symbol'
|
14
|
+
require 'wardrobe/plugins/coercible/refinements/time'
|
15
|
+
|
16
|
+
module Wardrobe
|
17
|
+
class Attribute
|
18
|
+
using Plugins::Coercible::Refinements
|
19
|
+
|
20
|
+
def coerce(val)
|
21
|
+
klass.coerce(val, self)
|
22
|
+
rescue Plugins::Coercible::Refinements::UnsupportedError => e
|
23
|
+
raise e.class,
|
24
|
+
"Can't coerce #{val.class} `#{val}` into #{klass}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
register_setter(
|
29
|
+
name: :coercer,
|
30
|
+
priority: -100,
|
31
|
+
use_if: ->(_atr) { true },
|
32
|
+
setter: lambda do |value, atr, _instance|
|
33
|
+
atr.coerce(value)
|
34
|
+
end
|
35
|
+
)
|
36
|
+
|
37
|
+
module Plugins
|
38
|
+
module Coercible
|
39
|
+
extend Wardrobe::Plugin
|
40
|
+
option :coerce, Boolean, default: true, setter: :coercer
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def coerce(val, _atr)
|
44
|
+
return new if val.nil?
|
45
|
+
return new(**val) if val.is_a?(Hash)
|
46
|
+
new(val)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
register_plugin(:coercible, Plugins::Coercible)
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Configurable
|
6
|
+
class ConfigurableStore < Store
|
7
|
+
using ImmutableInstanceMethods
|
8
|
+
def register(name, klass)
|
9
|
+
mutate do
|
10
|
+
store.merge!(name => klass.new.freeze)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(name, &blk)
|
15
|
+
if frozen?
|
16
|
+
dup.update(name, &blk)
|
17
|
+
else
|
18
|
+
duplicate = @store[name].mutate(&blk)
|
19
|
+
@store = @store.merge(name => duplicate)
|
20
|
+
freeze
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'configurable/configurable_store'
|
4
|
+
|
5
|
+
module Wardrobe
|
6
|
+
module Plugins
|
7
|
+
module Configurable
|
8
|
+
extend Wardrobe::Plugin
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
extend Forwardable
|
12
|
+
def_delegators :@wardrobe_stores, :configurable_store
|
13
|
+
|
14
|
+
def self.extended(base)
|
15
|
+
super
|
16
|
+
base.wardrobe_stores do
|
17
|
+
add_store(:configurable_store, ConfigurableStore)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def configurable(name, blk_name, klass)
|
22
|
+
wardrobe_stores do
|
23
|
+
@configurable_store = configurable_store.register(name, klass)
|
24
|
+
end
|
25
|
+
|
26
|
+
define_singleton_method(name) do
|
27
|
+
wardrobe_stores.configurable_store[name]
|
28
|
+
end
|
29
|
+
|
30
|
+
define_singleton_method(blk_name) do |&blk|
|
31
|
+
wardrobe_stores do
|
32
|
+
@configurable_store = configurable_store.update(name, &blk)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
register_plugin(:configurable, Plugins::Configurable)
|
40
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module Default
|
6
|
+
extend Wardrobe::Plugin
|
7
|
+
|
8
|
+
Wardrobe.register_setter(
|
9
|
+
name: :default_setter,
|
10
|
+
priority: 5,
|
11
|
+
use_if: ->(atr) { !atr.options[:default].nil? },
|
12
|
+
setter: lambda do |value, atr, instance|
|
13
|
+
if !value.nil? && ![{},[]].include?(value)
|
14
|
+
value
|
15
|
+
else
|
16
|
+
default = atr.options[:default]
|
17
|
+
case default
|
18
|
+
when Symbol
|
19
|
+
default.match?(/default$/) ? instance.send(default) : default
|
20
|
+
when Proc
|
21
|
+
default.arity.zero? ? default.call : default.call(instance)
|
22
|
+
else
|
23
|
+
default
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
)
|
28
|
+
|
29
|
+
option :default, Object, setter: :default_setter
|
30
|
+
end
|
31
|
+
end
|
32
|
+
register_plugin(:default, Plugins::Default)
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module DirtyTracker
|
6
|
+
extend Wardrobe::Plugin
|
7
|
+
|
8
|
+
Wardrobe.register_setter(
|
9
|
+
name: :dirty_tracker,
|
10
|
+
priority: 25,
|
11
|
+
use_if: ->(atr) { atr.options[:track] },
|
12
|
+
setter: lambda do |value, atr, instance|
|
13
|
+
return value if instance._initializing?
|
14
|
+
instance._dirty! if value != instance._get_attribute_value(atr)
|
15
|
+
value
|
16
|
+
end
|
17
|
+
)
|
18
|
+
|
19
|
+
Wardrobe.register_getter(
|
20
|
+
name: :dirty_tracker,
|
21
|
+
priority: 5,
|
22
|
+
use_if: ->(atr) { atr.options[:track] },
|
23
|
+
getter: lambda do |value, atr, instance|
|
24
|
+
instance._register_get(atr, value)
|
25
|
+
value
|
26
|
+
end
|
27
|
+
)
|
28
|
+
|
29
|
+
option :track, Boolean, default: true, setter: :dirty_tracker,
|
30
|
+
getter: :dirty_tracker#setter(10,&setter_proc)
|
31
|
+
|
32
|
+
module InstanceMethods
|
33
|
+
def _changed?
|
34
|
+
_fetched_attributes.delete_if do |atr, val|
|
35
|
+
if val == :wardrobe_instance
|
36
|
+
_dirty! if _get_attribute_value(atr)._changed?
|
37
|
+
else
|
38
|
+
_dirty! if _get_attribute_value(atr).hash != val
|
39
|
+
end
|
40
|
+
true
|
41
|
+
end
|
42
|
+
@changed ||= false
|
43
|
+
end
|
44
|
+
|
45
|
+
def _dirty!
|
46
|
+
@changed = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def _register_get(atr, value)
|
50
|
+
_fetched_attributes[atr] ||= if value.respond_to?(:_changed?)
|
51
|
+
:wardrobe_instance
|
52
|
+
else
|
53
|
+
value.hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def _fetched_attributes
|
60
|
+
@_fetched_attributes ||= {}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
register_plugin(:dirty_tracker, Plugins::DirtyTracker)
|
66
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Wardrobe
|
2
|
+
module Plugins
|
3
|
+
module Equality
|
4
|
+
extend Wardrobe::Plugin
|
5
|
+
|
6
|
+
option :include_in_equality, Boolean, default: true
|
7
|
+
|
8
|
+
module InstanceMethods
|
9
|
+
def ==(other)
|
10
|
+
_attribute_store.all? do |_name, atr|
|
11
|
+
return true unless atr.options[:include_in_equality]
|
12
|
+
send(atr.name) == other.send(atr.name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
register_plugin(:equality, Plugins::Equality)
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Wardrobe
|
2
|
+
module Plugins
|
3
|
+
module HtmlInitializer
|
4
|
+
extend Wardrobe::Plugin
|
5
|
+
|
6
|
+
option :html_selector, Proc
|
7
|
+
|
8
|
+
def self.parse(html)
|
9
|
+
parser.parse(html)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.parser
|
13
|
+
@parser ||= begin
|
14
|
+
unless defined? Nokogiri
|
15
|
+
require 'nokogiri'
|
16
|
+
end
|
17
|
+
Nokogiri::HTML
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module InstanceMethods
|
22
|
+
def initialize(html = nil, **args)
|
23
|
+
if html
|
24
|
+
define_singleton_method(:_attribute_init) do |atr, doc, _name|
|
25
|
+
value = atr.options[:html_selector].call(doc, atr, self)
|
26
|
+
send(atr.setter_name, value)
|
27
|
+
end
|
28
|
+
html = HtmlInitializer.parse(html) if html.is_a?(String)
|
29
|
+
_wardrobe_init(html)
|
30
|
+
else
|
31
|
+
super(**args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
register_plugin(:html_initializer, Plugins::HtmlInitializer)
|
38
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Wardrobe
|
5
|
+
module Plugins
|
6
|
+
module ImmutableInstanceMethods
|
7
|
+
IMMUTABLE_CORE_CLASSES = Set.new(
|
8
|
+
[NilClass, TrueClass, FalseClass, Integer, Symbol]
|
9
|
+
)
|
10
|
+
|
11
|
+
refine Object do
|
12
|
+
def _mutating?
|
13
|
+
instance_variable_defined?(:@_mutating) && @_mutating
|
14
|
+
end
|
15
|
+
|
16
|
+
def mutate(**args, &blk)
|
17
|
+
dup.instance_exec do
|
18
|
+
instance_variable_set(:@_mutating, true)
|
19
|
+
blk.call(self) if block_given?
|
20
|
+
args.each do |name, _value|
|
21
|
+
if (atr = _attribute_store[name])
|
22
|
+
_attribute_init(atr, args, name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
remove_instance_variable(:@_mutating)
|
26
|
+
deep_freeze
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def mutate!
|
32
|
+
return self if IMMUTABLE_CORE_CLASSES.include?(self.class)
|
33
|
+
dup.instance_exec do
|
34
|
+
instance_variable_set(:@_mutating, true)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
refine Hash do
|
41
|
+
def deep_freeze
|
42
|
+
each { |_k, v| v.deep_freeze }
|
43
|
+
freeze
|
44
|
+
end
|
45
|
+
end
|
46
|
+
refine String do
|
47
|
+
def deep_freeze
|
48
|
+
freeze
|
49
|
+
end
|
50
|
+
end
|
51
|
+
refine Logger do
|
52
|
+
def deep_freeze
|
53
|
+
freeze
|
54
|
+
end
|
55
|
+
end
|
56
|
+
refine Set do
|
57
|
+
def deep_freeze
|
58
|
+
# Refinements does not work here when using &:deep_freeze like in array
|
59
|
+
each { |item| item.deep_freeze }
|
60
|
+
remove_instance_variable(:@_mutating) if instance_variable_defined?(:@_mutating)
|
61
|
+
freeze
|
62
|
+
end
|
63
|
+
end
|
64
|
+
refine Array do
|
65
|
+
def deep_freeze
|
66
|
+
each(&:deep_freeze)
|
67
|
+
remove_instance_variable(:@_mutating) if instance_variable_defined?(:@_mutating)
|
68
|
+
freeze
|
69
|
+
end
|
70
|
+
|
71
|
+
def mutate!
|
72
|
+
dup.instance_exec do
|
73
|
+
instance_variable_set(:@_mutating, true)
|
74
|
+
map!(&:mutate!)
|
75
|
+
self
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
IMMUTABLE_CORE_CLASSES.each do |klass|
|
80
|
+
refine klass do
|
81
|
+
def deep_freeze
|
82
|
+
freeze
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
refine Wardrobe::InstanceMethods do
|
87
|
+
def mutate!
|
88
|
+
dup.instance_exec do
|
89
|
+
instance_variable_set(:@_mutating, true)
|
90
|
+
self
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def deep_freeze
|
95
|
+
_attribute_store.each do |_name, atr|
|
96
|
+
instance_variable_get(atr.ivar_name).deep_freeze
|
97
|
+
end
|
98
|
+
remove_instance_variable(:@_mutating) if instance_variable_defined?(:@_mutating)
|
99
|
+
freeze
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
module Immutable
|
105
|
+
extend Wardrobe::Plugin
|
106
|
+
|
107
|
+
# This plugin will disable all setters. A "set(key, value)" will be enabled. Should we also use hash storage?
|
108
|
+
# This should also depend on the optonal_setter plugin and set it to default on
|
109
|
+
|
110
|
+
Wardrobe.register_setter(
|
111
|
+
name: :disable_setter_for_immutable_plugin,
|
112
|
+
priority: -101,
|
113
|
+
use_if: ->(_atr) { true },
|
114
|
+
setter: lambda do |value, atr, instance|
|
115
|
+
return value if instance._initializing? || !instance.frozen?
|
116
|
+
raise NoMethodError, <<~eos
|
117
|
+
undefined method `#{atr.name}=' for #{instance}.
|
118
|
+
The instance is immutable. Use `#mutate(key, value)' or `#mutate { |obj| obj.#{atr.name} = #{value.inspect}}'
|
119
|
+
eos
|
120
|
+
end
|
121
|
+
)
|
122
|
+
|
123
|
+
Wardrobe.register_getter(
|
124
|
+
name: :dup_when_using_set_block,
|
125
|
+
priority: 100,
|
126
|
+
use_if: ->(_atr) { true },
|
127
|
+
getter: lambda do |value, atr, instance|
|
128
|
+
using ImmutableInstanceMethods
|
129
|
+
if instance._initializing?
|
130
|
+
value
|
131
|
+
elsif instance._mutating?
|
132
|
+
instance._set_attribute_value(atr, value.mutate!)
|
133
|
+
# instance.instance_variable_set(atr.ivar_name, value.mutate!)
|
134
|
+
else
|
135
|
+
value
|
136
|
+
end
|
137
|
+
end
|
138
|
+
)
|
139
|
+
|
140
|
+
option :immutable, Boolean, default: true,
|
141
|
+
setter: :disable_setter_for_immutable_plugin,
|
142
|
+
getter: :dup_when_using_set_block
|
143
|
+
|
144
|
+
module InstanceMethods
|
145
|
+
using ImmutableInstanceMethods
|
146
|
+
def initialize(**hash)
|
147
|
+
super
|
148
|
+
_data.deep_freeze if instance_variable_defined?(:@_data)
|
149
|
+
freeze
|
150
|
+
end
|
151
|
+
|
152
|
+
def mutate(**args, &blk)
|
153
|
+
super
|
154
|
+
end
|
155
|
+
|
156
|
+
def _attribute_init(atr, hash, name)
|
157
|
+
super
|
158
|
+
send(atr.name).deep_freeze
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
register_plugin(:immutable, Plugins::Immutable)
|
164
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'presenter/refinements'
|
4
|
+
module Wardrobe
|
5
|
+
module Plugins
|
6
|
+
module IvyPresenter
|
7
|
+
extend Wardrobe::Plugin
|
8
|
+
|
9
|
+
option :preset, Set
|
10
|
+
option :source, Boolean
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def _ivy_present(**args)
|
14
|
+
attributes_hash = IvyPresenter.build_attributes_hash(_attribute_store, **args)
|
15
|
+
_present(attributes: attributes_hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# using Refinements
|
20
|
+
# def _present(wardrobe: nil)
|
21
|
+
# result = {}
|
22
|
+
# _attribute_store.store.each do |key, atr|
|
23
|
+
# if wardrobe.nil? || (wardrobe && wardrobe.key?(key))
|
24
|
+
# result[key] = send(atr.name)._present(wardrobe: (wardrobe[key] if wardrobe))
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# result
|
28
|
+
# end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.build_attributes_hash(set, preset: nil, source: nil)
|
32
|
+
result = {}
|
33
|
+
set.each do |key, atr|
|
34
|
+
next unless atr.options[:preset]&.include?(preset) || atr.options[:source] == source
|
35
|
+
result[key] = nil
|
36
|
+
end
|
37
|
+
result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
register_plugin(:ivy_presenter, Plugins::IvyPresenter)
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wardrobe
|
4
|
+
module Plugins
|
5
|
+
module JsonInitializer
|
6
|
+
extend Wardrobe::Plugin
|
7
|
+
|
8
|
+
def self.parse(json)
|
9
|
+
raise InvalidJsonError unless json.match(/\A[\[{].+[\]}]\z/m)
|
10
|
+
case parser.to_s
|
11
|
+
when 'JSON'
|
12
|
+
parser.parse(json, symbolize_names: true)
|
13
|
+
when 'MultiJson'
|
14
|
+
parser.load(json, symbolize_keys: true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.parser
|
19
|
+
@parser ||= select_parser
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.select_parser
|
23
|
+
return MultiJson if defined? MultiJson
|
24
|
+
return JSON if defined? JSON
|
25
|
+
require 'json'
|
26
|
+
JSON
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
def initialize(json_string = nil, **args)
|
31
|
+
if json_string
|
32
|
+
super(**JsonInitializer.parse(json_string))
|
33
|
+
else
|
34
|
+
super(**args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
register_plugin(:json_initializer, Plugins::JsonInitializer)
|
41
|
+
end
|