phlexible 0.1.0 → 0.2.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/.rubocop.yml +3 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +4 -0
- data/README.md +112 -17
- data/fixtures/dummy/app/views/articles/link.rb +2 -2
- data/lib/phlexible/alias_view.rb +3 -2
- data/lib/phlexible/page_title.rb +41 -0
- data/lib/phlexible/rails/a_element.rb +12 -0
- data/lib/phlexible/rails/action_controller/implicit_render.rb +55 -0
- data/lib/phlexible/rails/button_to.rb +82 -0
- data/lib/phlexible/rails/responder.rb +27 -0
- data/lib/phlexible/rails.rb +9 -1
- data/lib/phlexible/version.rb +1 -1
- data/lib/phlexible.rb +1 -0
- metadata +11 -8
- data/lib/phlexible/rails/anchor_element.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de2a41c9efa577146733f7e842f503818c594d726995a8f62fd39f5e87749b61
|
4
|
+
data.tar.gz: 89306b9e9ca8b83ed58a4b598197cabe3c745c554645c0555b684c8dc653ac39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87c4b0ece2ba0ce51a1b8d865a48fe09dfcd41cec04ee9f862fb05c8e82140946ebabf71056fb863fd38364bf1a80e836d2024a2dd6d80893a50c4f637231e7a
|
7
|
+
data.tar.gz: 2fdfd0d7c354df97d8d932bc4eb695876bb87a6434a54b16ce0de729050225acbdbb564e04feb7b6315dc2bde0ee5987456ba67ed684bbfc660c7fae04723356
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -123,6 +123,9 @@ GEM
|
|
123
123
|
phlex (>= 1, < 2)
|
124
124
|
rails (>= 6.1, < 8)
|
125
125
|
zeitwerk (~> 2)
|
126
|
+
phlex-testing-nokogiri (0.1.0)
|
127
|
+
nokogiri (~> 1.13)
|
128
|
+
phlex (>= 0.5)
|
126
129
|
racc (1.6.2)
|
127
130
|
rack (2.2.5)
|
128
131
|
rack-test (2.0.2)
|
@@ -188,6 +191,7 @@ PLATFORMS
|
|
188
191
|
|
189
192
|
DEPENDENCIES
|
190
193
|
combustion
|
194
|
+
phlex-testing-nokogiri
|
191
195
|
phlexible!
|
192
196
|
rake (~> 13.0)
|
193
197
|
rubocop (~> 1.21)
|
data/README.md
CHANGED
@@ -16,14 +16,61 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
16
16
|
|
17
17
|
### Rails
|
18
18
|
|
19
|
-
#### `
|
19
|
+
#### `ActionController::ImplicitRender`
|
20
|
+
|
21
|
+
Adds support for default and `action_missing` rendering of Phlex views. So instead of this:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class UsersController
|
25
|
+
def index
|
26
|
+
render Views::Users::Index.new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
You can do this:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class UsersController
|
35
|
+
include Phlexible::Rails::ActionController::ImplicitRender
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
#### `Responder`
|
40
|
+
|
41
|
+
If you use [Responders](https://github.com/heartcombo/responders), Phlexible provides a responder to
|
42
|
+
support implicit rendering similar to `ActionController::ImplicitRender` above. It will render the
|
43
|
+
Phlex view using `respond_with` if one exists, and fall back to default rendering.
|
44
|
+
|
45
|
+
Just include it in your ApplicationResponder:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class ApplicationResponder < ActionController::Responder
|
49
|
+
include Phlexible::Rails::Responder
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Then simply `respond_with` in your action method as normal:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
class UsersController < ApplicationController
|
57
|
+
def new
|
58
|
+
respond_with User.new
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
This responder requires the use of `ActionController::ImplicitRender`, so dont't forget to include
|
64
|
+
that in your `ApplicationController`.
|
65
|
+
|
66
|
+
#### `AElement`
|
20
67
|
|
21
68
|
No need to call Rails `link_to` helper, when you can simply render an anchor tag directly with
|
22
69
|
Phlex. But unfortunately that means you lose some of the magic that `link_to` provides. Especially
|
23
70
|
the automatic resolution of URL's and Rails routes.
|
24
71
|
|
25
|
-
The `Phlexible::Rails::
|
26
|
-
|
72
|
+
The `Phlexible::Rails::AElement` module passes through the `href` attribute to Rails `url_for`
|
73
|
+
helper. So you can do this:
|
27
74
|
|
28
75
|
```ruby
|
29
76
|
Rails.application.routes.draw do
|
@@ -33,14 +80,48 @@ end
|
|
33
80
|
|
34
81
|
```ruby
|
35
82
|
class MyView < Phlex::HTML
|
36
|
-
|
83
|
+
include Phlexible::Rails::AElement
|
37
84
|
|
38
|
-
|
39
|
-
|
40
|
-
|
85
|
+
def template
|
86
|
+
a(href: :articles) { 'View articles' }
|
87
|
+
end
|
41
88
|
end
|
42
89
|
```
|
43
90
|
|
91
|
+
#### 'ButtonTo`
|
92
|
+
|
93
|
+
Generates a form containing a single button that submits to the URL created by the set of options.
|
94
|
+
|
95
|
+
It is similar to Rails `button_to` helper, which accepts the value/content of the button as the
|
96
|
+
first argument, and a URL or route helper as the second argument.
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
Phlexible::Rails::ButtonTo.new 'My Button', :root
|
100
|
+
```
|
101
|
+
|
102
|
+
Alternatively you can pass a block; the result of which will be used as the value of the button.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
Phlexible::Rails::ButtonTo.new(:root) { 'Go Home 👉' }
|
106
|
+
```
|
107
|
+
|
108
|
+
The url argument accepts the same options as Rails `url_for`.
|
109
|
+
|
110
|
+
The form submits a POST request by default. You can specify a different HTTP verb via the :method
|
111
|
+
option.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
Phlexible::Rails::ButtonTo.new 'My Button', :root, method: :patch
|
115
|
+
```
|
116
|
+
|
117
|
+
##### Options
|
118
|
+
|
119
|
+
- `:class` - Specify the HTML class name of the button (not the form).
|
120
|
+
- `:form_class` - Specify the HTML class name of the form (default: 'button_to').
|
121
|
+
- `:data` - This option can be used to add custom data attributes.
|
122
|
+
- `:method` - Symbol of the HTTP verb. Supported verbs are :post (default), :get, :delete, :patch,
|
123
|
+
and :put.
|
124
|
+
|
44
125
|
### `AliasView`
|
45
126
|
|
46
127
|
Create an alias at a given `element`, to the given view class.
|
@@ -49,11 +130,11 @@ So instead of:
|
|
49
130
|
|
50
131
|
```ruby
|
51
132
|
class MyView < Phlex::HTML
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
133
|
+
def template
|
134
|
+
div do
|
135
|
+
render My::Awesome::Component.new
|
56
136
|
end
|
137
|
+
end
|
57
138
|
end
|
58
139
|
```
|
59
140
|
|
@@ -61,18 +142,32 @@ You can instead do:
|
|
61
142
|
|
62
143
|
```ruby
|
63
144
|
class MyView < Phlex::HTML
|
64
|
-
|
145
|
+
extend Phlexible::AliasView
|
65
146
|
|
66
|
-
|
147
|
+
alias_view :awesome, -> { My::Awesome::Component }
|
67
148
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
149
|
+
def template
|
150
|
+
div do
|
151
|
+
awesome
|
72
152
|
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
### PageTitle
|
158
|
+
|
159
|
+
Helper to assist in defining page titles within Phlex views. Also includes support for nested views,
|
160
|
+
where each desendent view class will have its title prepended to the page title. Simply assign the
|
161
|
+
title to the `page_title` class variable:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
class MyView
|
165
|
+
self.page_title = 'My Title'
|
73
166
|
end
|
74
167
|
```
|
75
168
|
|
169
|
+
Then call the `page_title` method in the `<head>` of your page.
|
170
|
+
|
76
171
|
## Development
|
77
172
|
|
78
173
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Views::Articles::Link < Phlex::HTML
|
4
|
-
include Phlexible::Rails::
|
4
|
+
include Phlexible::Rails::AElement
|
5
5
|
|
6
6
|
def template
|
7
|
-
a(href: :root) { 'A link to root' }
|
7
|
+
a(href: :root, class: :foo) { 'A link to root' }
|
8
8
|
end
|
9
9
|
end
|
data/lib/phlexible/alias_view.rb
CHANGED
@@ -30,8 +30,9 @@ module Phlexible
|
|
30
30
|
#
|
31
31
|
module AliasView
|
32
32
|
def alias_view(element, view_class)
|
33
|
-
define_method element do |*args, **kwargs, &
|
34
|
-
render view_class.call.new(*args, **kwargs, &
|
33
|
+
define_method element do |*args, **kwargs, &blk|
|
34
|
+
render view_class.call.new(*args, **kwargs), &blk
|
35
|
+
# view_class.call.new(*args, **kwargs).call(@_target, view_context: @_view_context, parent: self, &blk)
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexible
|
4
|
+
#
|
5
|
+
# Helper to assist in defining page titles within Phlex views. Also includes support for nested
|
6
|
+
# views, where each desendent view class will have its title prepended to the page title. Simply
|
7
|
+
# assign the title to the `page_title` class variable:
|
8
|
+
#
|
9
|
+
# class MyView
|
10
|
+
# self.page_title = 'My Title'
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Then call the `page_title` method in the <head> of your page.
|
14
|
+
#
|
15
|
+
module PageTitle
|
16
|
+
def self.included(base)
|
17
|
+
base.class_eval do
|
18
|
+
self.class.attr_accessor :page_title
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def page_title
|
25
|
+
title = []
|
26
|
+
|
27
|
+
klass = self.class
|
28
|
+
while klass.respond_to?(:page_title)
|
29
|
+
title << if klass.page_title.is_a?(Proc)
|
30
|
+
instance_exec(&klass.page_title)
|
31
|
+
else
|
32
|
+
klass.page_title
|
33
|
+
end
|
34
|
+
|
35
|
+
klass = klass.superclass
|
36
|
+
end
|
37
|
+
|
38
|
+
title.compact.join(' - ')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Adds support for default and action_missing rendering of Phlex views. So instead of this:
|
4
|
+
#
|
5
|
+
# class UsersController
|
6
|
+
# def index
|
7
|
+
# render Views::Users::Index.new
|
8
|
+
# end
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# You can do this:
|
12
|
+
#
|
13
|
+
# class UsersController
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
module Phlexible
|
17
|
+
module Rails
|
18
|
+
module ActionController
|
19
|
+
module ImplicitRender
|
20
|
+
NUFFIN = 'NUFFIN'
|
21
|
+
|
22
|
+
def default_render
|
23
|
+
render_view_class || super
|
24
|
+
end
|
25
|
+
|
26
|
+
def render_view_class(view_options = NUFFIN, render_options = {})
|
27
|
+
klass = render_options&.key?(:action) ? phlex_view(render_options[:action]) : phlex_view
|
28
|
+
return unless klass
|
29
|
+
|
30
|
+
render view_options == NUFFIN ? klass.new : klass.new(view_options), render_options
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def method_for_action(action_name)
|
36
|
+
if action_method?(action_name)
|
37
|
+
action_name
|
38
|
+
elsif phlex_view
|
39
|
+
'_handle_view_class'
|
40
|
+
elsif respond_to?(:action_missing, true)
|
41
|
+
'_handle_action_missing'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def _handle_view_class(*_args)
|
46
|
+
render_view_class
|
47
|
+
end
|
48
|
+
|
49
|
+
def phlex_view(action_name = @_action_name)
|
50
|
+
"views/#{controller_path}/#{action_name}".classify.safe_constantize
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Generates a form containing a single button that submits to the URL created by the set of options.
|
4
|
+
# Similar to Rails `button_to` helper.
|
5
|
+
#
|
6
|
+
# The form submits a POST request by default. You can specify a different HTTP verb via the :method
|
7
|
+
# option.
|
8
|
+
module Phlexible
|
9
|
+
module Rails
|
10
|
+
module ButtonToConcerns
|
11
|
+
BUTTON_TAG_METHOD_VERBS = %w[patch put delete].freeze
|
12
|
+
DEFAULT_OPTIONS = { method: 'post', form_class: 'button_to' }.freeze
|
13
|
+
|
14
|
+
def initialize(name = nil, url = nil, options = nil)
|
15
|
+
@name = name
|
16
|
+
@url = url
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
21
|
+
def template(&block)
|
22
|
+
if block_given?
|
23
|
+
@options = @url
|
24
|
+
@url = @name
|
25
|
+
@name = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
action = helpers.url_for(@url)
|
29
|
+
@options = DEFAULT_OPTIONS.merge((@options || {}).symbolize_keys)
|
30
|
+
|
31
|
+
method = (@options.delete(:method).presence || method_for_options(@options)).to_s
|
32
|
+
form_method = method == 'get' ? 'get' : 'post'
|
33
|
+
|
34
|
+
form action: action, class: @options.delete(:form_class), method: form_method do
|
35
|
+
method_tag method
|
36
|
+
form_method == 'post' && token_input(action, method.empty? ? 'post' : method)
|
37
|
+
|
38
|
+
block_given? ? button(**button_attrs, &block) : button(**button_attrs) { @name }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def button_attrs
|
46
|
+
{
|
47
|
+
type: 'submit',
|
48
|
+
**@options
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_for_options(options)
|
53
|
+
if options.is_a?(Array)
|
54
|
+
method_for_options(options.last)
|
55
|
+
elsif options.respond_to?(:persisted?)
|
56
|
+
options.persisted? ? :patch : :post
|
57
|
+
elsif options.respond_to?(:to_model)
|
58
|
+
method_for_options(options.to_model)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def token_input(action, method)
|
63
|
+
return unless helpers.protect_against_forgery?
|
64
|
+
|
65
|
+
name = helpers.request_forgery_protection_token.to_s
|
66
|
+
value = helpers.form_authenticity_token(form_options: { action: action, method: method })
|
67
|
+
|
68
|
+
input type: 'hidden', name: name, value: value, autocomplete: 'off'
|
69
|
+
end
|
70
|
+
|
71
|
+
def method_tag(method)
|
72
|
+
return unless BUTTON_TAG_METHOD_VERBS.include?(method)
|
73
|
+
|
74
|
+
input type: 'hidden', name: '_method', value: method.to_s, autocomplete: 'off'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class ButtonTo < Phlex::HTML
|
79
|
+
include ButtonToConcerns
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexible
|
4
|
+
module Rails
|
5
|
+
module Responder
|
6
|
+
# Overridden to support implicit rendering of phlex views.
|
7
|
+
def default_render
|
8
|
+
if @default_response
|
9
|
+
@default_response.call(options)
|
10
|
+
elsif !get? && has_errors?
|
11
|
+
render_phlex_view options.merge(status: :unprocessable_entity)
|
12
|
+
else
|
13
|
+
render_phlex_view options
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Render the Phlex view with the current resource. Falls back to default controller rendering if
|
18
|
+
# no Phlex view exists.
|
19
|
+
#
|
20
|
+
# @see Phlexible::Rails::ActionController::ImplicitRender#render_view_class
|
21
|
+
def render_phlex_view(options)
|
22
|
+
controller.render_view_class(@resource, options) || controller.render(options)
|
23
|
+
end
|
24
|
+
alias render render_phlex_view
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/phlexible/rails.rb
CHANGED
@@ -4,6 +4,14 @@ require 'phlex-rails'
|
|
4
4
|
|
5
5
|
module Phlexible
|
6
6
|
module Rails
|
7
|
-
autoload :
|
7
|
+
autoload :Responder, 'phlexible/rails/responder'
|
8
|
+
autoload :AElement, 'phlexible/rails/a_element'
|
9
|
+
|
10
|
+
autoload :ButtonTo, 'phlexible/rails/button_to'
|
11
|
+
autoload :ButtonToConcerns, 'phlexible/rails/button_to'
|
12
|
+
|
13
|
+
module ActionController
|
14
|
+
autoload :ImplicitRender, 'phlexible/rails/action_controller/implicit_render'
|
15
|
+
end
|
8
16
|
end
|
9
17
|
end
|
data/lib/phlexible/version.rb
CHANGED
data/lib/phlexible.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phlexible
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Moss
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -38,8 +38,8 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.4'
|
41
|
-
description: A bunch of helpers and goodies intended to make life with
|
42
|
-
|
41
|
+
description: A bunch of helpers and goodies intended to make life with Phlex even
|
42
|
+
easier!
|
43
43
|
email:
|
44
44
|
- joel@developwithstyle.com
|
45
45
|
executables: []
|
@@ -67,8 +67,12 @@ files:
|
|
67
67
|
- fixtures/rails_helper.rb
|
68
68
|
- lib/phlexible.rb
|
69
69
|
- lib/phlexible/alias_view.rb
|
70
|
+
- lib/phlexible/page_title.rb
|
70
71
|
- lib/phlexible/rails.rb
|
71
|
-
- lib/phlexible/rails/
|
72
|
+
- lib/phlexible/rails/a_element.rb
|
73
|
+
- lib/phlexible/rails/action_controller/implicit_render.rb
|
74
|
+
- lib/phlexible/rails/button_to.rb
|
75
|
+
- lib/phlexible/rails/responder.rb
|
72
76
|
- lib/phlexible/version.rb
|
73
77
|
homepage: https://github.com/joelmoss/phlexible
|
74
78
|
licenses:
|
@@ -86,7 +90,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
90
|
requirements:
|
87
91
|
- - ">="
|
88
92
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
93
|
+
version: 3.0.0
|
90
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
95
|
requirements:
|
92
96
|
- - ">="
|
@@ -96,6 +100,5 @@ requirements: []
|
|
96
100
|
rubygems_version: 3.4.1
|
97
101
|
signing_key:
|
98
102
|
specification_version: 4
|
99
|
-
summary: A bunch of helpers and goodies intended to make life with
|
100
|
-
even easier!
|
103
|
+
summary: A bunch of helpers and goodies intended to make life with Phlex even easier!
|
101
104
|
test_files: []
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlexible
|
4
|
-
module Rails
|
5
|
-
module AnchorElement
|
6
|
-
# @override Calls `url_for` for the :href attribute.
|
7
|
-
def a(**attributes, &block)
|
8
|
-
attributes[:href] = helpers.url_for(attributes[:href]) if attributes.key?(:href)
|
9
|
-
|
10
|
-
super(**attributes, &block)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|