magic-decorator 0.1.0 → 0.2.0

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: 05eb0d1c9c1f08235269894f1ce906ca355d65239d9a708ad68078d9ebce7159
4
- data.tar.gz: 679f113b552348de25243303de4550cda0774dd5e284bd4ec64c94c79471aecf
3
+ metadata.gz: 910cb75b4dedfce18a494e39779b65821642feb96a24f3cda7228deea96ccd04
4
+ data.tar.gz: 98603bb63196fa955b4aec3172ff6326f5be10371b0fffb96404a9f5cd84a86b
5
5
  SHA512:
6
- metadata.gz: fb7bd90db8afc2235cc5a1b7f572b316a5494888a34a07052a7d77a2b20abc8d07444dca8e2e8436bb0a1dd6e8e61f8ddf15c80ed54ac5ee956a99e90bfbac4d
7
- data.tar.gz: ddac7d0a18af55a99a561812660847f6935dd849c42287dd397466c520c392742580331526b33b98efe9a64020a9622e6dee92e60e177f08c8c0a5c4851310c3
6
+ metadata.gz: 4b4bfb9bbb3838ac81dc35527caddd9d42d86887e1019ad75c7aad5f21af03607a1540523555aa0669baef7705a989af99487d3fa0a925fe8fedca96749070e3
7
+ data.tar.gz: 9f1cc4b1b608fd9b795b2a296c7919e513ed0a9ea7a9c0d1957fe23e5f5b537edc7ea30985ccc02ce72ca5fac1c7707d8b44769502a4f702e426dd26c2ce1260
data/CHANGELOG.md CHANGED
@@ -1,10 +1,31 @@
1
+ ## [0.2.0] — 2024-10-17
2
+
3
+ ### Changed
4
+
5
+ - For almost any method called on a decorated object, both its result and `yield`ed arguments get decorated.
6
+ Some methods aren’t meant to be decorated though:
7
+ - `deconstruct` & `deconstruct_keys` for _pattern matching_,
8
+ - _converting_ methods: those starting with `to_`,
9
+ - _system_ methods: those starting with `_`.
10
+
11
+ ### Added
12
+
13
+ - `Magic::Decorator::Base.undecorated` to exclude methods from being decorated automagically.
14
+
15
+ #### Default decorators
16
+
17
+ - `EnumerableDecorator` to decorate `Enumerable`s.
18
+ - enables _splat_ operator: `*decorated` ,
19
+ - enables _double-splat_ operator: `**decorated`,
20
+ - enumerating methods yield decorated items.
21
+
1
22
  ## [0.1.0] — 2024-10-13
2
23
 
3
24
  ### Added
4
25
 
5
26
  - `Magic::Decorator::Base` — a basic decorator class.
6
27
  - `Magic::Decoratable` to be included in decoratable classes.
7
- - `#decorate`,
8
- - `#decorate!`,
9
- - `#decorated`,
10
- - `#decorated?`.
28
+ - `#decorate`,
29
+ - `#decorate!`,
30
+ - `#decorated`,
31
+ - `#decorated?`.
data/README.md CHANGED
@@ -47,20 +47,55 @@ person.name # => "John Smith"
47
47
  This module adds three methods to decorate an object.
48
48
  Decorator class is being inferred automatically.
49
49
  When no decorator is found,
50
- * `#decorate` returns `nil`,
51
- * `#decorate!` raises `Magic::Lookup::Error`,
52
- * `#decorated` returns the original object.
50
+ - `#decorate` returns `nil`,
51
+ - `#decorate!` raises `Magic::Lookup::Error`,
52
+ - `#decorated` returns the original object.
53
53
 
54
54
  One can test for the object is actually decorated with `#decorated?`.
55
55
 
56
56
  ```ruby
57
- 'with no decorator for String'.decorated.decorated? # => false
58
- ['with a decorator for Array'].decorated.decorated? # => true
57
+ 'with no decorator for String'.decorated
58
+ .decorated? # => false
59
+ ['with a decorator for Array'].decorated
60
+ .decorated? # => true
59
61
  ```
60
62
 
61
- #### Magic
63
+ ## Magic
62
64
 
63
- `Decoratable` is mixed into `Object` by default. That means that effectively any object is `Decoratable`.
65
+ ### Decoratable scope
66
+
67
+ `Magic::Decoratable` is mixed into `Object` by default. It means that effectively any object is _magically decoratable_.
68
+
69
+ ### Decoration expansion
70
+
71
+ For almost any method called on a decorated object, both its result and `yield`ed arguments get decorated.
72
+
73
+ ```ruby
74
+ 'with no decorator for String'.decorated.chars
75
+ .decorated? # => false
76
+ ['with a decorator for Array'].decorated.map(&:chars).first.grep(/\S/).group_by(&:upcase).transform_values(&:size).sort_by(&:last).reverse.first(5).map(&:first)
77
+ .decorated? # => true
78
+ ```
79
+
80
+ #### Undecorated methods
81
+
82
+ Some methods aren’t meant to be decorated though:
83
+
84
+ - `deconstruct` & `deconstruct_keys` for _pattern matching_,
85
+ - _converting_ methods: those starting with `to_`,
86
+ - _system_ methods: those starting with `_`.
87
+
88
+ #### `undecorated` modifier
89
+
90
+ `Magic::Decorator::Base.undecorated` can be used to exclude methods from being decorated automagically.
91
+
92
+ ```ruby
93
+ class MyDecorator < Magic::Decorator::Base
94
+ undecorated %i[to_s inspect]
95
+ undecorated :raw_method
96
+ undecorated :m1, :m2
97
+ end
98
+ ```
64
99
 
65
100
  ### Decorator class inference
66
101
 
@@ -72,6 +107,33 @@ powered by [Magic Lookup](
72
107
  For example, `MyNamespace::MyModel.new.decorate` looks for `MyNamespace::MyModelDecorator` first.
73
108
  When missing, it further looks for decorators for its ancestor classes, up to `ObjectDecorator`.
74
109
 
110
+ ### Default decorators
111
+
112
+ #### `EnumerableDecorator`
113
+
114
+ It automagically decorates all its decoratable items.
115
+
116
+ ```ruby
117
+ [1, [2], { 3 => 4 }, '5'].decorated
118
+ .map &:decorated? # => [false, true, true, false]
119
+
120
+ { 1 => 2, [3] => [4] }.decorated.keys
121
+ .map &:decorated? # => [false, true]
122
+ { 1 => 2, [3] => [4] }.decorated.values
123
+ .map &:decorated? # => [false, true]
124
+
125
+ { 1 => 2, [3] => [4] }.decorated[1]
126
+ .decorated? # => false
127
+ { 1 => 2, [3] => [4] }.decorated[[3]]
128
+ .decorated? # => true
129
+ ```
130
+
131
+ ##### Side effects for decorated collections
132
+
133
+ - enables _splat_ operator: `*decorated` ,
134
+ - enables _double-splat_ operator: `**decorated`,
135
+ - enumerating methods yield decorated items.
136
+
75
137
  ## Development
76
138
 
77
139
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EnumerableDecorator < Magic::Decorator::Base
4
+ def to_a(...) = super.map &:decorated
5
+ def to_ary(...) = super.map &:decorated
6
+
7
+ def to_h(...) = super.to_h { |*h| h.map! &:decorated }
8
+ def to_hash(...) = super.to_h { |*h| h.map! &:decorated }
9
+ end
@@ -9,9 +9,51 @@ module Magic
9
9
 
10
10
  class << self
11
11
  def name_for(object_class) = "#{object_class}Decorator"
12
+
13
+ private
14
+
15
+ def undecorated method, *methods
16
+ return [ method, *methods ].map { undecorated _1 } if
17
+ methods.any?
18
+ return undecorated *method if
19
+ method.is_a? Array
20
+ raise TypeError, "#{method} is not a symbol nor a string" unless
21
+ method in Symbol | String
22
+
23
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
24
+ def #{method}(...) = __getobj__.#{method}(...)
25
+ RUBY
26
+ end
12
27
  end
13
28
 
14
29
  def decorated? = true
30
+
31
+ undecorated %i[
32
+ deconstruct
33
+ deconstruct_keys
34
+ ]
35
+
36
+ def method_missing(method, ...)
37
+ return super if method.start_with? 'to_' # converter
38
+ return super if method.start_with? '_' # system
39
+
40
+ if block_given?
41
+ super { |*args| yield *args.map(&:decorated) }
42
+ else
43
+ super
44
+ end.decorated
45
+ rescue NoMethodError => error
46
+ __raise__ error.class.new(
47
+ error.message.sub(self.class.name, __getobj__.class.name),
48
+ error.name,
49
+ error.args,
50
+ # error.private_call?, # FIXME: not implemented in TruffleRuby
51
+
52
+ receiver: __getobj__
53
+ ).tap {
54
+ _1.set_backtrace error.backtrace[2..] # FIXME: use `backtrace_locations` with Ruby 3.4+
55
+ }
56
+ end
15
57
  end
16
58
  end
17
59
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Magic
4
4
  module Decorator
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
@@ -18,3 +18,5 @@ module Magic
18
18
  def name_for(...) = Base.name_for(...)
19
19
  end
20
20
  end
21
+
22
+ require 'enumerable_decorator'
@@ -0,0 +1,9 @@
1
+ class EnumerableDecorator[unchecked out Element]
2
+ include _Each[Element, void]
3
+ include Enumerable[Element]
4
+
5
+ def to_ary: () -> ::Array[Element]
6
+
7
+ def to_hash: () -> ::Hash[untyped, untyped]
8
+ | [T, U] () { (Element) -> [T, U] } -> ::Hash[T, U]
9
+ end
@@ -4,6 +4,12 @@ module Magic
4
4
  extend Lookup
5
5
 
6
6
  def decorated?: -> bool
7
+
8
+ private
9
+
10
+ def self.undecorated: (interned) -> Symbol
11
+ | (interned, interned, *interned) -> Array[Symbol]
12
+ | (Array[interned]) -> Array[Symbol]
7
13
  end
8
14
  end
9
15
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magic-decorator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Senko
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2024-10-13 00:00:00.000000000 Z
10
+ date: 2024-10-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: magic-lookup
@@ -38,11 +38,13 @@ files:
38
38
  - LICENSE.txt
39
39
  - README.md
40
40
  - Rakefile
41
+ - lib/enumerable_decorator.rb
41
42
  - lib/magic/decoratable.rb
42
43
  - lib/magic/decorator.rb
43
44
  - lib/magic/decorator/authors.rb
44
45
  - lib/magic/decorator/base.rb
45
46
  - lib/magic/decorator/version.rb
47
+ - sig/enumerable_decorator.rbs
46
48
  - sig/gem.rbs
47
49
  - sig/magic/decoratable.rbs
48
50
  - sig/magic/decorator.rbs
@@ -53,7 +55,7 @@ licenses:
53
55
  metadata:
54
56
  homepage_uri: https://github.com/Alexander-Senko/magic-decorator
55
57
  source_code_uri: https://github.com/Alexander-Senko/magic-decorator
56
- changelog_uri: https://github.com/Alexander-Senko/magic-decorator/blob/v0.1.0/CHANGELOG.md
58
+ changelog_uri: https://github.com/Alexander-Senko/magic-decorator/blob/v0.2.0/CHANGELOG.md
57
59
  rdoc_options: []
58
60
  require_paths:
59
61
  - lib
@@ -61,7 +63,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
61
63
  requirements:
62
64
  - - "~>"
63
65
  - !ruby/object:Gem::Version
64
- version: '3.1'
66
+ version: '3.2'
65
67
  required_rubygems_version: !ruby/object:Gem::Requirement
66
68
  requirements:
67
69
  - - ">="