magic-presenter 0.2.0 → 0.4.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 +4 -4
- data/README.md +42 -0
- data/{lib → app/models/concerns}/magic/presentable.rb +8 -0
- data/config/initializers/action_controller.rb +14 -0
- data/config/initializers/action_view.rb +41 -0
- data/lib/magic/presenter/base.rb +20 -0
- data/lib/magic/presenter/global_id.rb +35 -0
- data/lib/magic/presenter/helpers.rb +53 -0
- data/lib/magic/presenter/version.rb +1 -1
- data/lib/magic/presenter.rb +18 -5
- metadata +12 -9
- data/config/initializers/loading.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ca84b456b21332f6eeba63a5667ed1c8cf7ac97d59141192f138d1698c504bf
|
4
|
+
data.tar.gz: c380ea14f2ddd44150ae71095063e895486d33077521fe1067fca81d4c4b36b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 728dbf1b11115069bb58bfb47c81258f358d22c3570c35dc4a5e7e4d9429a2fa5a6dc3237d0ef0e6a0442d88ca7e9b405e481bd5ea2da90c618800593c8f5155
|
7
|
+
data.tar.gz: f014751ea9cf0470fd1387f25f63b56291a63a434abd177ca0e1aaab27ba1d9a4da4e590354b729fcd12a40fa19f125866daedfba4bd904f37e8d48b3c281ea6
|
data/README.md
CHANGED
@@ -68,6 +68,35 @@ See the help for more info:
|
|
68
68
|
|
69
69
|
$ bin/rails generate presenter --help
|
70
70
|
|
71
|
+
### View helpers
|
72
|
+
|
73
|
+
A presenter can use any helpers via `#helpers` (aliased as `#h`) both in class and instance methods:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class PersonPresenter < Magic::Presenter::Base
|
77
|
+
def self.links
|
78
|
+
[ h.link_to('All', model_class) ]
|
79
|
+
end
|
80
|
+
|
81
|
+
def link(...)
|
82
|
+
helpers.link_to(name, self, ...)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
A view context must be set to enable helpers.
|
88
|
+
It’s done automagically [wherever possible](#helpers).
|
89
|
+
However, one can set it explicitly anywhere:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
Magic::Presenter.with view_context: ApplicationController.new.view_context do
|
93
|
+
# put the code that uses helpers within presenters here
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
> [!NOTE]
|
98
|
+
> A valid `request` may be needed for URL helpers to get host info.
|
99
|
+
|
71
100
|
## 🧙 Magic
|
72
101
|
|
73
102
|
It’s based on [Magic Decorator](
|
@@ -103,6 +132,19 @@ When in doubt, one can use `Magic::Presenter.name_for`:
|
|
103
132
|
Magic::Presenter.name_for Person # => "PersonPresenter"
|
104
133
|
```
|
105
134
|
|
135
|
+
### In views
|
136
|
+
|
137
|
+
> [!IMPORTANT]
|
138
|
+
> Every object passed to views is decorated automagically.
|
139
|
+
> This involves both implicit instance variables and `locals` passed explicitly.
|
140
|
+
|
141
|
+
### Helpers
|
142
|
+
|
143
|
+
View context is set automagically to enable helpers:
|
144
|
+
- in views,
|
145
|
+
- in controller actions,
|
146
|
+
- in mailer actions.
|
147
|
+
|
106
148
|
## Development
|
107
149
|
|
108
150
|
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,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
presenter_context = proc do
|
4
|
+
around_action :set_presenter_context
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def set_presenter_context(&)
|
9
|
+
Magic::Presenter.with view_context:, &
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load :action_controller, &presenter_context
|
14
|
+
ActiveSupport.on_load :action_mailer, &presenter_context
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ActiveSupport.on_load :action_view do # rubocop:disable Metrics/BlockLength
|
4
|
+
concerning :DecoratedAssignments, prepend: true do
|
5
|
+
def assign(assignments, ...)
|
6
|
+
decorate assignments
|
7
|
+
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def _run(method, template, locals, ...)
|
12
|
+
decorate locals
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def decorate objects
|
20
|
+
objects
|
21
|
+
.transform_values!(&:decorated)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
concerning :PresenterContext, prepend: true do
|
26
|
+
def in_rendering_context(...)
|
27
|
+
Magic::Presenter::Base.with view_context: self do
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def decorate(...)
|
35
|
+
super
|
36
|
+
.each_value
|
37
|
+
.grep(Magic::Presenter::Base)
|
38
|
+
.each { _1.view_context = self }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/magic/presenter/base.rb
CHANGED
@@ -17,6 +17,9 @@ module Magic
|
|
17
17
|
# If not found, it looks for presenters of its ancestor classes,
|
18
18
|
# up to `ObjectPresenter`.
|
19
19
|
class Base < Decorator::Base
|
20
|
+
include GlobalID if defined? ::GlobalID
|
21
|
+
prepend Helpers if defined? ::ActionView
|
22
|
+
|
20
23
|
class << self
|
21
24
|
def name_for object_class
|
22
25
|
object_class
|
@@ -25,6 +28,23 @@ module Magic
|
|
25
28
|
.delete_suffix('Record')
|
26
29
|
.then { "#{_1}Presenter" }
|
27
30
|
end
|
31
|
+
|
32
|
+
def model_class
|
33
|
+
Presentable.classes
|
34
|
+
.select { Presenter.for(_1) == self }
|
35
|
+
.sole
|
36
|
+
rescue Enumerable::SoleItemExpectedError => error
|
37
|
+
raise Lookup::Error, "#{error.message
|
38
|
+
.sub('items', 'model classes')
|
39
|
+
.sub('item', 'model class')
|
40
|
+
} for #{self}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def descendants
|
44
|
+
Magic.eager_load :presenters
|
45
|
+
|
46
|
+
super
|
47
|
+
end
|
28
48
|
end
|
29
49
|
end
|
30
50
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Magic
|
4
|
+
module Presenter
|
5
|
+
# = Active Job integration
|
6
|
+
#
|
7
|
+
# Active Job [accepts Active Record objects](
|
8
|
+
# http://edgeguides.rubyonrails.org/active_job_basics.html#globalid
|
9
|
+
# ).
|
10
|
+
# An object passed to a background job must implement [Global ID](
|
11
|
+
# https://github.com/rails/globalid
|
12
|
+
# ).
|
13
|
+
#
|
14
|
+
# This module implements Global ID for decorated models by defining
|
15
|
+
# `.find` method that uses the one from a model class and decorates
|
16
|
+
# the result.
|
17
|
+
# This means one can pass decorated objects to background jobs and
|
18
|
+
# get them decorated when deserialized.
|
19
|
+
module GlobalID
|
20
|
+
extend ActiveSupport::Concern
|
21
|
+
|
22
|
+
included do
|
23
|
+
include ::GlobalID::Identification
|
24
|
+
end
|
25
|
+
|
26
|
+
class_methods do
|
27
|
+
def find(...)
|
28
|
+
model_class
|
29
|
+
.find(...)
|
30
|
+
.decorated
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Magic
|
4
|
+
module Presenter
|
5
|
+
module Helpers # :nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# The Magic::Presenter::Helpers::MissingContext exception is
|
9
|
+
# raised when no view context to run helpers in has been set.
|
10
|
+
class MissingContext < RuntimeError
|
11
|
+
def message
|
12
|
+
<<~TEXT
|
13
|
+
missing view context
|
14
|
+
You should set Magic::Presenter.view_context first
|
15
|
+
TEXT
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
prepended do
|
20
|
+
class_attribute :view_context
|
21
|
+
end
|
22
|
+
|
23
|
+
class_methods do
|
24
|
+
include Helpers
|
25
|
+
|
26
|
+
alias __raise__ raise
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def helpers
|
32
|
+
view_context or
|
33
|
+
__raise__ MissingContext
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method :h, :helpers
|
37
|
+
|
38
|
+
def method_missing(method, ...)
|
39
|
+
super
|
40
|
+
rescue NoMethodError
|
41
|
+
__raise__ unless view_context
|
42
|
+
__raise__ unless helpers.respond_to? method
|
43
|
+
|
44
|
+
helpers.send(method, ...)
|
45
|
+
end
|
46
|
+
|
47
|
+
def respond_to_missing?(method, ...)
|
48
|
+
super or
|
49
|
+
(helpers if view_context).respond_to?(method, ...)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/magic/presenter.rb
CHANGED
@@ -6,16 +6,29 @@ require_relative 'presenter/version'
|
|
6
6
|
require_relative 'presenter/engine'
|
7
7
|
|
8
8
|
module Magic # :nodoc:
|
9
|
-
autoload :Presentable, 'magic/presentable'
|
10
|
-
|
11
9
|
# Presentation layer for Rails models
|
12
10
|
module Presenter
|
13
11
|
autoload :Base, 'magic/presenter/base'
|
12
|
+
autoload :Helpers, 'magic/presenter/helpers'
|
13
|
+
autoload :GlobalID, 'magic/presenter/global_id'
|
14
14
|
autoload :Generator, 'generators/magic/presenter/generator'
|
15
15
|
|
16
|
-
|
16
|
+
singleton_class.delegate *%i[
|
17
|
+
for name_for
|
18
|
+
view_context view_context=
|
19
|
+
], to: Base
|
20
|
+
end
|
21
|
+
|
22
|
+
module_function
|
23
|
+
|
24
|
+
def eager_load *scopes
|
25
|
+
return if Rails.application.config.eager_load
|
17
26
|
|
18
|
-
|
19
|
-
|
27
|
+
scopes
|
28
|
+
.map(&:to_s)
|
29
|
+
.map(&:pluralize)
|
30
|
+
.map { Rails.root / 'app' / _1 }
|
31
|
+
.select(&:exist?)
|
32
|
+
.each { Rails.autoloaders.main.eager_load_dir _1 }
|
20
33
|
end
|
21
34
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: magic-presenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Senko
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2024-10-
|
10
|
+
date: 2024-10-29 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -33,16 +33,16 @@ dependencies:
|
|
33
33
|
name: magic-decorator
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
35
35
|
requirements:
|
36
|
-
- - "
|
36
|
+
- - "~>"
|
37
37
|
- !ruby/object:Gem::Version
|
38
|
-
version: 0.3
|
38
|
+
version: '0.3'
|
39
39
|
type: :runtime
|
40
40
|
prerelease: false
|
41
41
|
version_requirements: !ruby/object:Gem::Requirement
|
42
42
|
requirements:
|
43
|
-
- - "
|
43
|
+
- - "~>"
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 0.3
|
45
|
+
version: '0.3'
|
46
46
|
description: Based on Magic Decorator, it’s meant to replace Draper.
|
47
47
|
email:
|
48
48
|
- Alexander.Senko@gmail.com
|
@@ -53,7 +53,9 @@ files:
|
|
53
53
|
- MIT-LICENSE
|
54
54
|
- README.md
|
55
55
|
- Rakefile
|
56
|
-
-
|
56
|
+
- app/models/concerns/magic/presentable.rb
|
57
|
+
- config/initializers/action_controller.rb
|
58
|
+
- config/initializers/action_view.rb
|
57
59
|
- config/initializers/presentable.rb
|
58
60
|
- config/initializers/rspec.rb
|
59
61
|
- lib/generators/magic/presenter/generator.rb
|
@@ -67,11 +69,12 @@ files:
|
|
67
69
|
- lib/generators/rspec/presenter/templates/presenter_spec.rb.tt
|
68
70
|
- lib/generators/test_unit/presenter/presenter_generator.rb
|
69
71
|
- lib/generators/test_unit/presenter/templates/presenter_test.rb.tt
|
70
|
-
- lib/magic/presentable.rb
|
71
72
|
- lib/magic/presenter.rb
|
72
73
|
- lib/magic/presenter/authors.rb
|
73
74
|
- lib/magic/presenter/base.rb
|
74
75
|
- lib/magic/presenter/engine.rb
|
76
|
+
- lib/magic/presenter/global_id.rb
|
77
|
+
- lib/magic/presenter/helpers.rb
|
75
78
|
- lib/magic/presenter/version.rb
|
76
79
|
- lib/tasks/magic/presenter_tasks.rake
|
77
80
|
homepage: https://github.com/Alexander-Senko/magic-presenter
|
@@ -80,7 +83,7 @@ licenses:
|
|
80
83
|
metadata:
|
81
84
|
homepage_uri: https://github.com/Alexander-Senko/magic-presenter
|
82
85
|
source_code_uri: https://github.com/Alexander-Senko/magic-presenter
|
83
|
-
changelog_uri: https://github.com/Alexander-Senko/magic-presenter/blob/v0.
|
86
|
+
changelog_uri: https://github.com/Alexander-Senko/magic-presenter/blob/v0.4.0/CHANGELOG.md
|
84
87
|
rdoc_options: []
|
85
88
|
require_paths:
|
86
89
|
- lib
|