chic 0.1.0 → 0.3.1
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/.rubocop.yml +8 -5
- data/README.md +300 -32
- data/bin/bundle +114 -0
- data/chic.gemspec +5 -3
- data/lib/chic/configuration.rb +8 -3
- data/lib/chic/errors.rb +3 -1
- data/lib/chic/formats.rb +20 -6
- data/lib/chic/formatters/nil.rb +1 -1
- data/lib/chic/presentable.rb +8 -0
- data/lib/chic/presents.rb +6 -3
- data/lib/chic/version.rb +1 -1
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29c1b7b66f93c55e96cd4f3399b52a89461f32212c5752df80f3413a5c8898ce
|
4
|
+
data.tar.gz: 3cd78efa318d5614ab2c37a02c986bbe2f27760e1710591b32059889aa1c7cd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 101121d9551d426c4c6d8e39b75f96def4ae7bd22955d1c0a93ff6509e755bcad33eb55bc1bfbae0cbdbffae03c1aea459c82fcbc3dfad0410b5db77960e73ea
|
7
|
+
data.tar.gz: edef9ddd2761744818b172b34f1d6942fc2d3fafaf81ec6edc600e26eab36bfafb75d55916830931b74cf1e20679f657c81babf326ee5bc75e8241d6cdb370e8
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,86 +1,354 @@
|
|
1
1
|
# Chic
|
2
2
|
|
3
|
-
|
3
|
+
An opinionated presentation layer comprising presenters and formatters.
|
4
|
+
|
5
|
+
Chic was borne out of a need for a simple PORO-style presentation layer for Rails applications to help DRY up formatting logic in views and view helpers.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
7
|
-
|
9
|
+
To get started, add this line to your Gemfile and install it using Bundler:
|
8
10
|
|
9
|
-
|
11
|
+
```ruby
|
12
|
+
gem 'chic'
|
13
|
+
```
|
10
14
|
|
11
|
-
|
15
|
+
## Usage
|
12
16
|
|
13
|
-
|
17
|
+
Though it is not a requirement, for ease of reference it is assumed that you will be using this library in a Rails application where you will be creating presenters for your models.
|
14
18
|
|
15
|
-
|
19
|
+
### Creating Presenters
|
16
20
|
|
17
|
-
|
21
|
+
Create a presenter by deriving from `Chic::Presenter`, and then declare which attributes should return formatters or presenters.
|
18
22
|
|
19
|
-
|
23
|
+
```ruby
|
24
|
+
# app/presenters/foo_presenter.rb
|
20
25
|
|
21
|
-
|
26
|
+
class FooPresenter < Chic::Presenter
|
27
|
+
formats :baz
|
28
|
+
|
29
|
+
presents bar: 'BarPresenter'
|
30
|
+
end
|
31
|
+
```
|
22
32
|
|
23
|
-
|
33
|
+
### Using Presenters
|
34
|
+
|
35
|
+
Include `Chic::Presentable` to make your objects presentable:
|
24
36
|
|
25
37
|
```ruby
|
26
|
-
|
38
|
+
# app/models/foo.rb
|
39
|
+
|
40
|
+
class Foo < ApplicationRecord
|
27
41
|
include Chic::Presentable
|
42
|
+
|
43
|
+
belongs_to :bar
|
28
44
|
end
|
29
45
|
```
|
30
46
|
|
31
|
-
|
47
|
+
Instantiate a presenter by calling `.present` on the presenter class, for example in a Rails view:
|
32
48
|
|
33
|
-
|
49
|
+
```erb
|
50
|
+
<% FooPresenter.present @foo do |foo_presenter| %>
|
51
|
+
<!-- ... -->
|
52
|
+
<% end %>
|
53
|
+
```
|
54
|
+
|
55
|
+
Collections can be presented using `.present_each`:
|
56
|
+
|
57
|
+
```erb
|
58
|
+
<% FooPresenter.present_each @foos do |foo_presenter, foo| %>
|
59
|
+
<!-- ... -->
|
60
|
+
<% end %>
|
61
|
+
```
|
62
|
+
|
63
|
+
You can also include the view helpers:
|
34
64
|
|
35
65
|
```ruby
|
36
|
-
|
37
|
-
|
66
|
+
module ApplicationHelper
|
67
|
+
include Chic::Helpers::View
|
38
68
|
end
|
39
69
|
```
|
40
70
|
|
41
|
-
|
71
|
+
Which will allow you to instantiate presenters without having to use the class name:
|
72
|
+
|
73
|
+
```erb
|
74
|
+
<% present @foo do |foo_presenter| %>
|
75
|
+
<!-- ... -->
|
76
|
+
<% end %>
|
77
|
+
|
78
|
+
<% present_each @foos do |foo_presenter, foo| %>
|
79
|
+
<!-- ... -->
|
80
|
+
<% end %>
|
81
|
+
```
|
82
|
+
|
83
|
+
See the [Conventions](#conventions) section below for more on using presenters.
|
84
|
+
|
85
|
+
### Creating Formatters
|
86
|
+
|
87
|
+
Formatters format values by deriving from `Chic::Formatters::Nil` and overriding the private `value` method:
|
42
88
|
|
43
89
|
```ruby
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
90
|
+
# app/formatters/date_time_formatter.rb
|
91
|
+
|
92
|
+
class DateTimeFormatter < Chic::Formatters::Nil
|
93
|
+
private
|
94
|
+
|
95
|
+
def value
|
96
|
+
return if object.blank?
|
97
|
+
|
98
|
+
object.strftime('%-d %b %Y %H:%M')
|
99
|
+
end
|
48
100
|
end
|
49
101
|
```
|
50
102
|
|
51
|
-
**Note:** You
|
103
|
+
**Note:** You should always return `nil` if the object being formatted is blank so that the `Nil` formatter behaves correctly.
|
52
104
|
|
53
|
-
|
105
|
+
Provide additional formatter options as chainable methods:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# app/formatters/date_time_formatter.rb
|
109
|
+
|
110
|
+
class DateTimeFormatter < Chic::Formatters::Nil
|
111
|
+
def format=(value)
|
112
|
+
@format = value
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def value
|
119
|
+
return if object.blank?
|
120
|
+
|
121
|
+
object.strftime(@format || '%-d %b %Y %H:%M')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
```
|
54
125
|
|
55
|
-
|
126
|
+
### Using Formatters
|
127
|
+
|
128
|
+
Declare formatted values in presenters using `formats`:
|
56
129
|
|
57
130
|
```ruby
|
131
|
+
# app/presenters/foo_presenter.rb
|
132
|
+
|
133
|
+
class FooPresenter < Chic::Presenter
|
134
|
+
formats :created_at,
|
135
|
+
with: 'DateTimeFormatter'
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
Render formatted values by calling `#to_s` on the formatter returned, which happens implicitly in Rails views for example:
|
140
|
+
|
141
|
+
```erb
|
58
142
|
<% FooPresenter.present @foo do |foo_presenter, _foo| %>
|
59
|
-
|
143
|
+
<%= foo_presenter.created_at %>
|
60
144
|
<% end %>
|
61
145
|
```
|
62
146
|
|
63
|
-
|
147
|
+
#### Configurable options
|
148
|
+
|
149
|
+
If the formatter derives from `Chic::Formatters::Nil`, then configure a blank value to be used:
|
64
150
|
|
65
151
|
```ruby
|
66
|
-
|
67
|
-
|
152
|
+
# app/presenters/foo_presenter.rb
|
153
|
+
|
154
|
+
class FooPresenter < Chic::Presenter
|
155
|
+
formats :created_at,
|
156
|
+
with: 'DateTimeFormatter',
|
157
|
+
options: {
|
158
|
+
blank_value: '(Not yet created)'
|
159
|
+
}
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
If the formatter supports additional options using chainable methods as described above, configure those options in the same way:
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
# app/presenters/foo_presenter.rb
|
167
|
+
|
168
|
+
class FooPresenter < Chic::Presenter
|
169
|
+
formats :created_at,
|
170
|
+
with: 'DateTimeFormatter',
|
171
|
+
options: {
|
172
|
+
format: '%-d %B %Y at %H:%M'
|
173
|
+
}
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
If needed, override those options where the formatted value is being rendered:
|
178
|
+
|
179
|
+
```erb
|
180
|
+
<% FooPresenter.present @foo do |foo_presenter, _foo| %>
|
181
|
+
<%= foo_presenter.created_at.format('%c').blank_value('–') %>
|
68
182
|
<% end %>
|
69
183
|
```
|
70
184
|
|
71
|
-
|
185
|
+
#### Named formatters
|
186
|
+
|
187
|
+
Optionally configure formatters with a name, for example in a Rails initializer:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
# config/initializers/chic.rb
|
191
|
+
|
192
|
+
require 'chic'
|
193
|
+
|
194
|
+
Chic.configure do |config|
|
195
|
+
config.formatters.merge!(
|
196
|
+
date_time: 'DateTimeFormatter'
|
197
|
+
)
|
198
|
+
end
|
199
|
+
```
|
200
|
+
|
201
|
+
Allowing you to refer to those formatters by name instead of by class:
|
72
202
|
|
73
203
|
```ruby
|
74
|
-
|
204
|
+
# app/presenters/foo_presenter.rb
|
205
|
+
|
206
|
+
class FooPresenter < Chic::Presenter
|
207
|
+
formats :created_at,
|
208
|
+
with: :date_time
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
## Logging
|
213
|
+
|
214
|
+
If a presenter class for an object you're trying to present can't be found, an entry at debug level will be made to the configured logger.
|
215
|
+
|
216
|
+
You can configure the logger to be used:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
# config/initializers/chic.rb
|
220
|
+
|
221
|
+
require 'chic'
|
222
|
+
|
223
|
+
Chic.configure do |config|
|
224
|
+
config.logger = Logger.new($stdout)
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
It may be beneficial to know about missing presenter classes sooner than later, in which case you can enable exceptions when it makes sense to do so – for example, a Rails application in any environment other than production:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
# config/initializers/chic.rb
|
232
|
+
|
233
|
+
require 'chic'
|
234
|
+
|
235
|
+
Chic.configure do |config|
|
236
|
+
config.raise_exceptions = Rails.env.production? == false
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
## Conventions
|
241
|
+
|
242
|
+
A few helpful conventions that have gone a long way to keep things maintainable.
|
243
|
+
|
244
|
+
### Naming presenter classes
|
245
|
+
|
246
|
+
Presenter class names are derived by appending `Presenter` to the `#model_name` or the class name of the object being presented. It is strongly recommended that you stick to this convention, but if you need to change it – for example you might have overridden `#model_name` – you can do so by defining a `#presenter_class` method:
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
# app/forms/user/sign_up_form.rb
|
250
|
+
|
251
|
+
class User::SignUpForm < User
|
252
|
+
include Chic::Presentable
|
253
|
+
|
254
|
+
def self.model_name
|
255
|
+
ActiveModel::Name.new(self, nil, 'User')
|
256
|
+
end
|
257
|
+
|
258
|
+
def presenter_class
|
259
|
+
User::SignUpFormPresenter
|
260
|
+
end
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
264
|
+
### Instantiate presenters in views only
|
265
|
+
|
266
|
+
Try not instantiate presenters outside of the view layer if possible.
|
267
|
+
|
268
|
+
**Don't**
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# app/controllers/foo_controller.rb
|
272
|
+
|
273
|
+
class FoosController < ApplicationController
|
274
|
+
def show
|
275
|
+
@foo = Foo.find(params[:id])
|
276
|
+
@foo_presenter = FooPresenter.new(@foo)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
```
|
280
|
+
|
281
|
+
```erb
|
282
|
+
<!-- app/views/foos/_show.html.erb -->
|
283
|
+
|
284
|
+
<%= link_to @foo_presenter.created_at, foo_path(@foo) %>
|
285
|
+
```
|
286
|
+
|
287
|
+
**Do**
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
# app/controllers/foo_controller.rb
|
291
|
+
|
292
|
+
class FoosController < ApplicationController
|
293
|
+
def show
|
294
|
+
@foo = Foo.find(params[:id])
|
295
|
+
end
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
```erb
|
300
|
+
<!-- app/views/foos/_show.html.erb -->
|
301
|
+
|
302
|
+
<% present @foo do |foo_presenter| %>
|
303
|
+
<%= link_to foo_presenter.created_at, foo_path(@foo) %>
|
304
|
+
<% end %>
|
305
|
+
```
|
306
|
+
|
307
|
+
### Keep presenter instances scoped to only the view in which they're being used
|
308
|
+
|
309
|
+
It can get messy if you pass presenter instances to other views, try avoid that if possible.
|
310
|
+
|
311
|
+
**Don't**
|
312
|
+
|
313
|
+
```erb
|
314
|
+
<% present @foo do |foo_presenter| %>
|
75
315
|
<!-- ... -->
|
316
|
+
<%= render partial: 'path/to/partial', locals: { foo: foo_presenter } %>
|
76
317
|
<% end %>
|
77
318
|
```
|
78
319
|
|
79
|
-
|
320
|
+
**Do**
|
80
321
|
|
81
|
-
```
|
82
|
-
<%
|
322
|
+
```erb
|
323
|
+
<% present @foo do |foo_presenter| %>
|
83
324
|
<!-- ... -->
|
325
|
+
<%= render partial: 'path/to/partial', locals: { foo: @foo } %>
|
326
|
+
<% end %>
|
327
|
+
```
|
328
|
+
|
329
|
+
### Use presenters and formatters for presentation only
|
330
|
+
|
331
|
+
It may be tempting to use presenters for conditional logic, but it's far better to use the original object for anything other than presentation.
|
332
|
+
|
333
|
+
**Don't**
|
334
|
+
|
335
|
+
```erb
|
336
|
+
<% present_each @foos do |foo_presenter, foo| %>
|
337
|
+
<% if foo_presenter.created_at %>
|
338
|
+
<%= link_to foo_presenter.created_at, foo_path(foo_presenter) %>
|
339
|
+
<% end
|
340
|
+
<% end %>
|
341
|
+
```
|
342
|
+
|
343
|
+
**Note:** Passing a presenter instance to a route helper as shown would only work if the presenter declared `id` as a formatted attribute. While this may work, it is not predictable behaviour.
|
344
|
+
|
345
|
+
**Do**
|
346
|
+
|
347
|
+
```erb
|
348
|
+
<% present_each @foos do |foo_presenter, foo| %>
|
349
|
+
<% if foo.created_at %>
|
350
|
+
<%= link_to foo_presenter.created_at, foo_path(foo) %>
|
351
|
+
<% end
|
84
352
|
<% end %>
|
85
353
|
```
|
86
354
|
|
data/bin/bundle
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "rubygems"
|
12
|
+
|
13
|
+
m = Module.new do
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def invoked_as_script?
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def env_var_version
|
21
|
+
ENV["BUNDLER_VERSION"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def cli_arg_version
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
27
|
+
bundler_version = nil
|
28
|
+
update_index = nil
|
29
|
+
ARGV.each_with_index do |a, i|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
31
|
+
bundler_version = a
|
32
|
+
end
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
34
|
+
bundler_version = $1
|
35
|
+
update_index = i
|
36
|
+
end
|
37
|
+
bundler_version
|
38
|
+
end
|
39
|
+
|
40
|
+
def gemfile
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
43
|
+
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lockfile
|
48
|
+
lockfile =
|
49
|
+
case File.basename(gemfile)
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
51
|
+
else "#{gemfile}.lock"
|
52
|
+
end
|
53
|
+
File.expand_path(lockfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
def lockfile_version
|
57
|
+
return unless File.file?(lockfile)
|
58
|
+
lockfile_contents = File.read(lockfile)
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
60
|
+
Regexp.last_match(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def bundler_version
|
64
|
+
@bundler_version ||=
|
65
|
+
env_var_version || cli_arg_version ||
|
66
|
+
lockfile_version
|
67
|
+
end
|
68
|
+
|
69
|
+
def bundler_requirement
|
70
|
+
return "#{Gem::Requirement.default}.a" unless bundler_version
|
71
|
+
|
72
|
+
bundler_gem_version = Gem::Version.new(bundler_version)
|
73
|
+
|
74
|
+
requirement = bundler_gem_version.approximate_recommendation
|
75
|
+
|
76
|
+
return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
|
77
|
+
|
78
|
+
requirement += ".a" if bundler_gem_version.prerelease?
|
79
|
+
|
80
|
+
requirement
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_bundler!
|
84
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
85
|
+
|
86
|
+
activate_bundler
|
87
|
+
end
|
88
|
+
|
89
|
+
def activate_bundler
|
90
|
+
gem_error = activation_error_handling do
|
91
|
+
gem "bundler", bundler_requirement
|
92
|
+
end
|
93
|
+
return if gem_error.nil?
|
94
|
+
require_error = activation_error_handling do
|
95
|
+
require "bundler/version"
|
96
|
+
end
|
97
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
98
|
+
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
|
99
|
+
exit 42
|
100
|
+
end
|
101
|
+
|
102
|
+
def activation_error_handling
|
103
|
+
yield
|
104
|
+
nil
|
105
|
+
rescue StandardError, LoadError => e
|
106
|
+
e
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
m.load_bundler!
|
111
|
+
|
112
|
+
if m.invoked_as_script?
|
113
|
+
load Gem.bin_path("bundler", "bundle")
|
114
|
+
end
|
data/chic.gemspec
CHANGED
@@ -23,8 +23,10 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
-
spec.
|
26
|
+
spec.required_ruby_version = '>= 2.7'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 2.3.10'
|
27
29
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
28
|
-
spec.add_development_dependency 'rake', '~>
|
29
|
-
spec.add_development_dependency 'rubocop', '
|
30
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.71'
|
30
32
|
end
|
data/lib/chic/configuration.rb
CHANGED
@@ -11,15 +11,20 @@ module Chic
|
|
11
11
|
nil: Formatters::Nil
|
12
12
|
}.freeze
|
13
13
|
|
14
|
-
attr_writer :
|
15
|
-
:
|
14
|
+
attr_writer :formatters,
|
15
|
+
:logger,
|
16
|
+
:raise_exceptions
|
16
17
|
|
17
18
|
def formatters
|
18
19
|
@formatters ||= FORMATTERS.dup
|
19
20
|
end
|
20
21
|
|
21
22
|
def logger
|
22
|
-
@logger ||= Logger.new(
|
23
|
+
@logger ||= Logger.new($stdout)
|
24
|
+
end
|
25
|
+
|
26
|
+
def raise_exceptions
|
27
|
+
@raise_exceptions == true
|
23
28
|
end
|
24
29
|
end
|
25
30
|
end
|
data/lib/chic/errors.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Chic
|
4
|
-
class
|
4
|
+
class ConfigFormatterNotValid < StandardError; end
|
5
5
|
class FormatsOptionsNotValid < StandardError; end
|
6
|
+
class PresenterClassNotDefined < StandardError; end
|
7
|
+
class PresentsOptionsNotValid < StandardError; end
|
6
8
|
end
|
data/lib/chic/formats.rb
CHANGED
@@ -41,7 +41,6 @@ module Chic
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# rubocop: disable Metrics/AbcSize
|
44
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
45
44
|
def _validate_formats_options_with!(attributes, options)
|
46
45
|
_raise_formats_options_not_valid '`with` must be a symbol or a class', attributes \
|
47
46
|
if options.key?(:with) && !options[:with].is_a?(Symbol) && !options[:with].is_a?(Class)
|
@@ -49,9 +48,8 @@ module Chic
|
|
49
48
|
_raise_formats_options_not_valid "`with` formatter :#{options[:with]} doesn't exist", attributes \
|
50
49
|
if options.key?(:with) && options[:with].is_a?(Symbol) && !Chic.configuration.formatters.key?(options[:with])
|
51
50
|
end
|
52
|
-
# rubocop: enable Metrics/AbcSize
|
53
|
-
# rubocop: enable Metrics/CyclomaticComplexity
|
54
51
|
|
52
|
+
# rubocop: enable Metrics/AbcSize
|
55
53
|
def _validate_formats_options_value!(attributes, options)
|
56
54
|
_raise_formats_options_not_valid '`value` must be a symbol or a lambda', attributes \
|
57
55
|
if options.key?(:value) && !options[:value].is_a?(Symbol) && !options[:value].is_a?(Proc)
|
@@ -70,17 +68,33 @@ module Chic
|
|
70
68
|
|
71
69
|
private
|
72
70
|
|
71
|
+
# rubocop: disable Metrics/MethodLength
|
73
72
|
def _formatters
|
74
|
-
Chic.configuration.formatters
|
73
|
+
@_formatters ||= Chic.configuration.formatters.reduce({}) do |result, (key, klass)|
|
74
|
+
formatter = case klass
|
75
|
+
when Class
|
76
|
+
klass
|
77
|
+
when String
|
78
|
+
klass.constantize
|
79
|
+
else
|
80
|
+
raise ConfigFormatterNotValid, "Configured formatter for #{key} must be a class or class-string"
|
81
|
+
end
|
82
|
+
|
83
|
+
result.merge(key => formatter)
|
84
|
+
end
|
75
85
|
end
|
86
|
+
# rubocop: enable Metrics/MethodLength
|
76
87
|
|
77
88
|
def _formats_options_value(attribute, options)
|
78
89
|
return object&.public_send(attribute) unless options.is_a?(Hash) && options.key?(:value)
|
79
90
|
|
80
|
-
|
91
|
+
case options[:value]
|
92
|
+
when Symbol
|
81
93
|
send(options[:value])
|
82
|
-
|
94
|
+
when Proc
|
83
95
|
instance_exec(&options[:value])
|
96
|
+
else
|
97
|
+
raise FormatsOptionsNotValid, "Options value for #{attribute} must be a symbol or a Proc"
|
84
98
|
end
|
85
99
|
end
|
86
100
|
|
data/lib/chic/formatters/nil.rb
CHANGED
data/lib/chic/presentable.rb
CHANGED
@@ -12,6 +12,8 @@ module Chic
|
|
12
12
|
|
13
13
|
module_function
|
14
14
|
|
15
|
+
# rubocop: disable Metrics/AbcSize
|
16
|
+
# rubocop: disable Metrics/MethodLength
|
15
17
|
def presenter_for(object)
|
16
18
|
if object.respond_to?(:presenter_class)
|
17
19
|
object.presenter_class
|
@@ -19,8 +21,14 @@ module Chic
|
|
19
21
|
"#{object&.model_name || object.class.name}Presenter".constantize
|
20
22
|
end
|
21
23
|
rescue NameError, LoadError
|
24
|
+
if Chic.configuration.raise_exceptions
|
25
|
+
raise PresenterClassNotDefined, "Couldn't find a presenter for '#{object.class.name}'"
|
26
|
+
end
|
27
|
+
|
22
28
|
Chic.configuration.logger&.debug "Couldn't find a presenter for '#{object.class.name}'"
|
23
29
|
nil
|
24
30
|
end
|
31
|
+
# rubocop: enable Metrics/AbcSize
|
32
|
+
# rubocop: enable Metrics/MethodLength
|
25
33
|
end
|
26
34
|
end
|
data/lib/chic/presents.rb
CHANGED
@@ -90,7 +90,7 @@ module Chic
|
|
90
90
|
|
91
91
|
def present(object, with: nil)
|
92
92
|
if object.is_a?(Enumerable)
|
93
|
-
object.map { |o| present(o, with) }
|
93
|
+
object.map { |o| present(o, with: with) }
|
94
94
|
elsif with.present?
|
95
95
|
with.new(object, context)
|
96
96
|
elsif object.nil?
|
@@ -115,10 +115,13 @@ module Chic
|
|
115
115
|
def _presents_options_value(attribute, options)
|
116
116
|
return object&.public_send(attribute) unless options.is_a?(Hash) && options.key?(:value)
|
117
117
|
|
118
|
-
|
118
|
+
case options[:value]
|
119
|
+
when Symbol
|
119
120
|
send(options[:value])
|
120
|
-
|
121
|
+
when Proc
|
121
122
|
instance_exec(&options[:value])
|
123
|
+
else
|
124
|
+
raise PresentsOptionsNotValid, "Options value for #{attribute} must be a symbol or a Proc"
|
122
125
|
end
|
123
126
|
end
|
124
127
|
end
|
data/lib/chic/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lawrance Shepstone
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.3.10
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 2.3.10
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '13.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '13.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: '0.71'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: '0.71'
|
69
69
|
description:
|
70
70
|
email:
|
71
71
|
- lawrance@collabcollective.com
|
@@ -81,6 +81,7 @@ files:
|
|
81
81
|
- LICENSE.txt
|
82
82
|
- README.md
|
83
83
|
- Rakefile
|
84
|
+
- bin/bundle
|
84
85
|
- bin/console
|
85
86
|
- bin/setup
|
86
87
|
- chic.gemspec
|
@@ -111,15 +112,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
112
|
requirements:
|
112
113
|
- - ">="
|
113
114
|
- !ruby/object:Gem::Version
|
114
|
-
version: '
|
115
|
+
version: '2.7'
|
115
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
117
|
requirements:
|
117
118
|
- - ">="
|
118
119
|
- !ruby/object:Gem::Version
|
119
120
|
version: '0'
|
120
121
|
requirements: []
|
121
|
-
|
122
|
-
rubygems_version: 2.7.6
|
122
|
+
rubygems_version: 3.1.2
|
123
123
|
signing_key:
|
124
124
|
specification_version: 4
|
125
125
|
summary: Opinionated presentation layer comprised of presenters and formatters.
|