phlexible 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: d6c45035fcfe50f425a2876e79f5973399b74b76f5923357dd02c355575d0eac
4
- data.tar.gz: 49e7ddc371aaad6e53316cdd4aa5bb0bfb76cda8f6fa1052779e40306d1e339a
3
+ metadata.gz: de2a41c9efa577146733f7e842f503818c594d726995a8f62fd39f5e87749b61
4
+ data.tar.gz: 89306b9e9ca8b83ed58a4b598197cabe3c745c554645c0555b684c8dc653ac39
5
5
  SHA512:
6
- metadata.gz: 9149698fef1b20d16ca297db60e847172a7454dcab4a53e2798a9cbe38f04ab5b87124453861c312f8002365a895b4e97b76494541568cd3c8c1714c5d99cf4b
7
- data.tar.gz: 7e4782383a6f3f542b460739c2e3ed7d27dbf677ab2dab2b73794bb12f0298e19833e63e329ab744e510a750f0db3465546ae5a6704503755c6fa54df034ec48
6
+ metadata.gz: 87c4b0ece2ba0ce51a1b8d865a48fe09dfcd41cec04ee9f862fb05c8e82140946ebabf71056fb863fd38364bf1a80e836d2024a2dd6d80893a50c4f637231e7a
7
+ data.tar.gz: 2fdfd0d7c354df97d8d932bc4eb695876bb87a6434a54b16ce0de729050225acbdbb564e04feb7b6315dc2bde0ee5987456ba67ed684bbfc660c7fae04723356
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.7
2
+ TargetRubyVersion: 3.0
3
3
  SuggestExtensions: false
4
4
  NewCops: enable
5
5
 
@@ -14,3 +14,5 @@ Lint/ConstantDefinitionInBlock:
14
14
  Style/ClassAndModuleChildren:
15
15
  Exclude:
16
16
  - fixtures/**/*
17
+ Metrics/MethodLength:
18
+ Max: 15
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  gem 'combustion'
9
+ gem 'phlex-testing-nokogiri'
9
10
  gem 'rake', '~> 13.0'
10
11
  gem 'rubocop', '~> 1.21'
11
12
  gem 'sus', '~> 0.16.0'
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
- #### `AnchorElement`
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::AnchorElement` module redefines the `a` tag, and passes through the `href`
26
- attribute to Rails `url_for` helper. So you can do this:
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
- include Phlexible::Rails::AnchorElement
83
+ include Phlexible::Rails::AElement
37
84
 
38
- def template
39
- a(href: :articles) { 'View articles' }
40
- end
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
- def template
53
- div do
54
- render My::Awesome::Component.new
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
- extend Phlexible::AliasView
145
+ extend Phlexible::AliasView
65
146
 
66
- alias_view :awesome, -> { My::Awesome::Component }
147
+ alias_view :awesome, -> { My::Awesome::Component }
67
148
 
68
- def template
69
- div do
70
- awesome
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::AnchorElement
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
@@ -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, &block|
34
- render view_class.call.new(*args, **kwargs, &block)
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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexible
4
+ module Rails
5
+ # Calls `url_for` for the `href` attribute.
6
+ module AElement
7
+ def a(href:, **kwargs, &block)
8
+ super(href: helpers.url_for(href), **kwargs, &block)
9
+ end
10
+ end
11
+ end
12
+ 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
@@ -4,6 +4,14 @@ require 'phlex-rails'
4
4
 
5
5
  module Phlexible
6
6
  module Rails
7
- autoload :AnchorElement, 'phlexible/rails/anchor_element'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlexible
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/phlexible.rb CHANGED
@@ -5,5 +5,6 @@ require 'phlex'
5
5
 
6
6
  module Phlexible
7
7
  autoload :AliasView, 'phlexible/alias_view'
8
+ autoload :PageTitle, 'phlexible/page_title'
8
9
  autoload :Rails, 'phlexible/rails'
9
10
  end
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.1.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-01-10 00:00:00.000000000 Z
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 [Phlex](https://phlex.fun)
42
- even easier!
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/anchor_element.rb
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: 2.7.0
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 [Phlex](https://phlex.fun)
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