magic-decorator 0.2.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 910cb75b4dedfce18a494e39779b65821642feb96a24f3cda7228deea96ccd04
4
- data.tar.gz: 98603bb63196fa955b4aec3172ff6326f5be10371b0fffb96404a9f5cd84a86b
3
+ metadata.gz: 84b46e61d09d011e36bfd527b1e99bf9b83a7d3d4844f9afcd11304abd5c7990
4
+ data.tar.gz: 22f50120c5b4b8b2887d28b3e909f698128c04a37293c9409d93411dddb1f2e3
5
5
  SHA512:
6
- metadata.gz: 4b4bfb9bbb3838ac81dc35527caddd9d42d86887e1019ad75c7aad5f21af03607a1540523555aa0669baef7705a989af99487d3fa0a925fe8fedca96749070e3
7
- data.tar.gz: 9f1cc4b1b608fd9b795b2a296c7919e513ed0a9ea7a9c0d1957fe23e5f5b537edc7ea30985ccc02ce72ca5fac1c7707d8b44769502a4f702e426dd26c2ce1260
6
+ metadata.gz: 0ad5fc1e9e7cf8fab9a75a792cc31f027e113e95f7968ee2f6502b113c4f8f943bf7f111fa599929896e6bf7a3a77cf7cf13d46c6fe58dd52e770fc109278d42
7
+ data.tar.gz: 743f50f6835be84d59028240ec9c306df79f8647c90d4b4c34d39eb9e462741cbfc3f0ebde4bea447c0a94b731e61a8e370c497d84821bfe9dfa4272771f3d8b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## [1.0.0] — 2024-11-23
2
+
3
+ This release marks the gem to be stable enough.
4
+
5
+ > [!NOTE]
6
+ > Nothing notable was changed in the code since the last version.
7
+
8
+ ### Documentation
9
+
10
+ - Added a section about overriding the defaults.
11
+ - Added a section about testing.
12
+
13
+
14
+ ## [0.3.0] — 2024-10-27
15
+
16
+ ### Added
17
+
18
+ - Improved extendability: one may override `Magic::Decoratable#decorator_base` to be used for lookups.
19
+ - `Magic::Decoratable.classes` for all the decoratables.
20
+
21
+ ### Fixed
22
+
23
+ - Failures on double decoration attempts.
24
+
25
+
1
26
  ## [0.2.0] — 2024-10-17
2
27
 
3
28
  ### Changed
@@ -19,6 +44,7 @@
19
44
  - enables _double-splat_ operator: `**decorated`,
20
45
  - enumerating methods yield decorated items.
21
46
 
47
+
22
48
  ## [0.1.0] — 2024-10-13
23
49
 
24
50
  ### Added
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # 🪄 Magic Decorator
2
2
 
3
+ ![GitHub Actions Workflow Status](
4
+ https://img.shields.io/github/actions/workflow/status/Alexander-Senko/magic-decorator/ci.yml
5
+ )
3
6
  ![Code Climate maintainability](
4
7
  https://img.shields.io/codeclimate/maintainability-percentage/Alexander-Senko/magic-decorator
5
8
  )
@@ -60,12 +63,40 @@ One can test for the object is actually decorated with `#decorated?`.
60
63
  .decorated? # => true
61
64
  ```
62
65
 
63
- ## Magic
66
+ ### Extending decorator logic
67
+
68
+ When extending `Magic::Decoratable`, one may override `#decorator_base` to be used for lookup.
69
+
70
+ ```ruby
71
+ class Special::Decorator < Magic::Decorator::Base
72
+ def self.name_for object_class
73
+ "Special::#{object_class}Decorator"
74
+ end
75
+ end
76
+
77
+ module Special::Decoratable
78
+ include Magic::Decoratable
79
+
80
+ private
81
+
82
+ def decorator_base = Special::Decorator
83
+ end
84
+
85
+ class Special::Model
86
+ include Special::Decoratable
87
+ end
88
+
89
+ Special::Model.new.decorate # looks for Special::Decorator descendants
90
+ ```
91
+
92
+ ## 🪄 Magic
64
93
 
65
94
  ### Decoratable scope
66
95
 
67
96
  `Magic::Decoratable` is mixed into `Object` by default. It means that effectively any object is _magically decoratable_.
68
97
 
98
+ One can use `Magic::Decoratable.classes` to see all the decoratable classes.
99
+
69
100
  ### Decoration expansion
70
101
 
71
102
  For almost any method called on a decorated object, both its result and `yield`ed arguments get decorated.
@@ -134,6 +165,51 @@ It automagically decorates all its decoratable items.
134
165
  - enables _double-splat_ operator: `**decorated`,
135
166
  - enumerating methods yield decorated items.
136
167
 
168
+ ## Overriding the magic
169
+
170
+ When one needs more complicated behavior than the default one or feels like [_explicit is better than implicit_](
171
+ https://peps.python.org/pep-0020/#the-zen-of-python
172
+ ).
173
+
174
+ ### Decorator class inference
175
+
176
+ One may override `#decorator` for any decoratable class, to be used instead of Magic Lookup.
177
+
178
+ - That could be as straightforward as a constant:
179
+
180
+ ```ruby
181
+ class Guest
182
+ private
183
+
184
+ def decorator = UserDecorator
185
+ end
186
+
187
+ guest.decorate # => instance of UserDecorator
188
+ ```
189
+
190
+ - Or, that could be virtually any logic:
191
+
192
+ ```ruby
193
+ class User
194
+ private
195
+
196
+ def decorator = admin? ? AdminDecorator : super
197
+ end
198
+
199
+ user.decorate # => instance of UserDecorator
200
+ admin.decorate # => instance of AdminDecorator
201
+ ```
202
+
203
+ ## Testing decorators
204
+
205
+ Testing a decorator is much like testing any other class.
206
+
207
+ To test whether an object is decorated one can use `#decorated?` method.
208
+
209
+ > [!NOTE]
210
+ > A decorated object equals the original one (`object.decorated == object`).
211
+ > Thus, any existing tests shouldn’t break when the objects being tested get decorated.
212
+
137
213
  ## Development
138
214
 
139
215
  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.
@@ -1,14 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/concern'
4
+
3
5
  module Magic
4
6
  module Decoratable
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def classes
11
+ ObjectSpace.each_object(Class)
12
+ .select { _1 < self }
13
+ end
14
+ end
15
+
16
+ extend ClassMethods
17
+
5
18
  def decorate = decorator&.new self
6
- def decorate! = decorate || raise(Lookup::Error.for self, Decorator)
19
+ def decorate! = decorate || raise(Lookup::Error.for self, decorator_base)
7
20
  def decorated = decorate || self
8
21
  def decorated? = false
9
22
 
10
23
  private
11
24
 
12
- def decorator = Decorator.for self.class
25
+ def decorator = decorator_base.for self.class
26
+ def decorator_base = Decorator
13
27
  end
14
28
  end
@@ -5,7 +5,8 @@ require 'delegate'
5
5
  module Magic
6
6
  module Decorator
7
7
  class Base < SimpleDelegator
8
- extend Lookup
8
+ extend Lookup
9
+ include Decoratable
9
10
 
10
11
  class << self
11
12
  def name_for(object_class) = "#{object_class}Decorator"
@@ -33,6 +34,8 @@ module Magic
33
34
  deconstruct_keys
34
35
  ]
35
36
 
37
+ private
38
+
36
39
  def method_missing(method, ...)
37
40
  return super if method.start_with? 'to_' # converter
38
41
  return super if method.start_with? '_' # system
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Magic
4
4
  module Decorator
5
- VERSION = '0.2.0'
5
+ VERSION = '1.0.0'
6
6
  end
7
7
  end
@@ -1,13 +1,16 @@
1
1
  module Magic
2
2
  module Decoratable
3
- def decorate: -> Decorator?
4
- def decorate!: -> Decorator
5
- def decorated: -> (Decorator | self)
3
+ def self.classes: () -> Array[Class]
6
4
 
7
- def decorated?: -> bool
5
+ def decorate: () -> Decorator?
6
+ def decorate!: () -> Decorator
7
+ def decorated: () -> (Decorator | self)
8
+
9
+ def decorated?: () -> bool
8
10
 
9
11
  private
10
12
 
11
- def decorator: -> Class?
13
+ def decorator: () -> Class?
14
+ def decorator_base: () -> Module
12
15
  end
13
16
  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.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Senko
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2024-10-17 00:00:00.000000000 Z
10
+ date: 2024-11-23 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: magic-lookup
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: activesupport
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
26
40
  description: 'SimpleDelegator on steroids: automatic delegation, decorator class inference,
27
41
  etc.'
28
42
  email:
@@ -55,7 +69,7 @@ licenses:
55
69
  metadata:
56
70
  homepage_uri: https://github.com/Alexander-Senko/magic-decorator
57
71
  source_code_uri: https://github.com/Alexander-Senko/magic-decorator
58
- changelog_uri: https://github.com/Alexander-Senko/magic-decorator/blob/v0.2.0/CHANGELOG.md
72
+ changelog_uri: https://github.com/Alexander-Senko/magic-decorator/blob/v1.0.0/CHANGELOG.md
59
73
  rdoc_options: []
60
74
  require_paths:
61
75
  - lib