phlex-rails 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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +13 -0
  3. data/.rubocop.yml +31 -0
  4. data/Appraisals +13 -0
  5. data/CONTRIBUTING.md +23 -0
  6. data/Gemfile +7 -4
  7. data/README.md +14 -30
  8. data/SECURITY.md +5 -0
  9. data/fixtures/dummy/app/assets/config/manifest.js +0 -0
  10. data/fixtures/dummy/app/components/comment_component.html.erb +14 -0
  11. data/fixtures/dummy/app/components/comment_component.rb +8 -0
  12. data/fixtures/dummy/app/components/reaction_component.html.erb +3 -0
  13. data/fixtures/dummy/app/components/reaction_component.rb +7 -0
  14. data/fixtures/dummy/app/controllers/articles_controller.rb +4 -0
  15. data/fixtures/dummy/app/controllers/comments_controller.rb +4 -0
  16. data/fixtures/dummy/app/views/application_view.rb +7 -0
  17. data/fixtures/dummy/app/views/articles/form.rb +15 -0
  18. data/fixtures/dummy/app/views/articles/index.html.erb +14 -0
  19. data/fixtures/dummy/app/views/articles/new.html.erb +1 -0
  20. data/fixtures/dummy/app/views/card.rb +15 -0
  21. data/fixtures/dummy/app/views/comments/comment.rb +25 -0
  22. data/fixtures/dummy/app/views/comments/index.html.erb +3 -0
  23. data/fixtures/dummy/app/views/comments/reaction.rb +17 -0
  24. data/fixtures/dummy/app/views/comments/show.html.erb +3 -0
  25. data/fixtures/dummy/app/views/heading.rb +9 -0
  26. data/fixtures/dummy/config/database.yml +3 -0
  27. data/fixtures/dummy/config/routes.rb +5 -0
  28. data/fixtures/dummy/config/storage.yml +3 -0
  29. data/fixtures/dummy/db/schema.rb +6 -0
  30. data/fixtures/dummy/log/.gitignore +1 -0
  31. data/fixtures/dummy/public/favicon.ico +0 -0
  32. data/fixtures/rails_helper.rb +9 -0
  33. data/fixtures/view_helper.rb +16 -0
  34. data/lib/generators/phlex/collection/USAGE +8 -0
  35. data/lib/generators/phlex/collection/collection_generator.rb +13 -0
  36. data/lib/generators/phlex/collection/templates/collection.rb.erb +16 -0
  37. data/lib/generators/phlex/controller/USAGE +10 -0
  38. data/lib/generators/phlex/controller/controller_generator.rb +54 -0
  39. data/lib/generators/phlex/controller/templates/controller.rb.erb +10 -0
  40. data/lib/generators/phlex/controller/templates/view.rb.erb +14 -0
  41. data/lib/generators/phlex/layout/USAGE +8 -0
  42. data/lib/generators/phlex/layout/layout_generator.rb +13 -0
  43. data/lib/generators/phlex/layout/templates/layout.rb.erb +31 -0
  44. data/lib/generators/phlex/page/USAGE +8 -0
  45. data/lib/generators/phlex/page/page_generator.rb +13 -0
  46. data/lib/generators/phlex/page/templates/page.rb.erb +13 -0
  47. data/lib/generators/phlex/table/USAGE +8 -0
  48. data/lib/generators/phlex/table/table_generator.rb +14 -0
  49. data/lib/generators/phlex/table/templates/table.rb.erb +11 -0
  50. data/lib/generators/phlex/view/USAGE +9 -0
  51. data/lib/generators/phlex/view/templates/view.rb.erb +14 -0
  52. data/lib/generators/phlex/view/view_generator.rb +23 -0
  53. data/lib/generators/rspec/view/templates/view_spec.rb.erb +10 -0
  54. data/lib/generators/rspec/view/view_generator.rb +14 -0
  55. data/lib/generators/test_unit/templates/view_test.rb.erb +15 -0
  56. data/lib/generators/test_unit/view_generator.rb +12 -0
  57. data/lib/install/phlex.rb +38 -0
  58. data/lib/phlex/rails/buffer.rb +18 -0
  59. data/lib/phlex/rails/engine.rb +10 -0
  60. data/lib/phlex/rails/form.rb +67 -0
  61. data/lib/phlex/rails/helpers.rb +126 -0
  62. data/lib/phlex/rails/layout.rb +16 -0
  63. data/lib/phlex/rails/renderable.rb +35 -0
  64. data/lib/phlex/rails/version.rb +3 -3
  65. data/lib/phlex/rails.rb +9 -6
  66. data/lib/phlex/testing/rails.rb +19 -0
  67. data/lib/phlex-rails.rb +3 -0
  68. data/lib/tasks/phlex_tasks.rake +11 -0
  69. data/phlex_logo.png +0 -0
  70. metadata +124 -16
  71. data/.gitignore +0 -8
  72. data/CHANGELOG.md +0 -5
  73. data/Rakefile +0 -12
  74. data/bin/console +0 -15
  75. data/bin/setup +0 -8
  76. data/phlex-rails.gemspec +0 -35
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generates a Phlex table collection view with the given name
3
+
4
+ Example:
5
+ rails generate phlex:collection Articles::Table --properties title author created_at
6
+
7
+ This will create:
8
+ app/views/articles/table.rb
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Generators
5
+ class TableGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+ class_option :properties, type: :array, default: []
8
+
9
+ def create_view
10
+ template "table.rb.erb", File.join("app/views", class_path, "#{file_name}.rb")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ <% module_namespacing do -%>
2
+ module Views
3
+ class <%= class_name %> < Phlex::HTML
4
+ include ApplicationView
5
+ include Phlex::Table
6
+
7
+ <% options["properties"].each do |property| %>
8
+ property "<%= property.humanize %>", &:<%= property.underscore %><% end %>
9
+ end
10
+ end
11
+ <% end %>
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generates a Phlex view with the given name
3
+
4
+ Example:
5
+ rails generate phlex:view Sidebar
6
+
7
+ This will create:
8
+ app/views/sidebar.rb
9
+ spec/views/sidebar_spec.rb or test/views/sidebar_test.rb
@@ -0,0 +1,14 @@
1
+ <% module_namespacing do -%>
2
+ module Views
3
+ class <%= class_name %> < Phlex::HTML
4
+ include ApplicationView
5
+
6
+ def template
7
+ <%= "# " unless @layout %>render Layout.new(title: "<%= class_name.gsub("::", " ").titlecase %>") do
8
+ h1 { "<%= class_name %>" }
9
+ p { "Find me in <%= @path %>" }
10
+ <%= "# " unless @layout %>end
11
+ end
12
+ end
13
+ end
14
+ <% end %>
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Generators
5
+ class ViewGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ def create_view
9
+ @layout = layout
10
+ @path = File.join("app/views", class_path, "#{file_name}.rb")
11
+ template "view.rb.erb", @path
12
+ end
13
+
14
+ private
15
+
16
+ def layout
17
+ ::Rails.root.join("app/views/layout.rb").exist?
18
+ end
19
+
20
+ hook_for :test_framework
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ require "rails_helper"
2
+
3
+ RSpec.describe Views::<%= class_name %>, type: :view do
4
+ pending "renders without problem" do
5
+ result = render(described_class.new)
6
+
7
+ expect(result.css("h1").text).to eq "<%= class_name %>"
8
+ expect(result.css("p").text).to eq "Find me in <%= @path %>"
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rspec
4
+ module Generators
5
+ class ViewGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ def create_view
9
+ @path = File.join("app/views", class_path, "#{file_name}.rb")
10
+ template "view_spec.rb.erb", File.join("spec/views", class_path, "#{file_name}_spec.rb")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ require "test_helper"
2
+ require "phlex/testing/rails"
3
+ require "phlex/testing/nokogiri"
4
+
5
+ class Views::<%= class_name %>Test < ActionView::TestCase
6
+ include Phlex::Testing::Rails::ViewHelper
7
+ include Phlex::Testing::Nokogiri::FragmentHelper
8
+
9
+ test "should render without problems" do
10
+ result = render(Views::<%= class_name %>.new)
11
+
12
+ assert_equal result.css("h1").first.text, "<%= class_name %>"
13
+ assert_equal result.css("p").first.text, "Find me in <%= @path %>"
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestUnit
4
+ class ViewGenerator < ::Rails::Generators::NamedBase
5
+ source_root File.expand_path("templates", __dir__)
6
+
7
+ def create_view
8
+ @path = File.join("app/views", class_path, "#{file_name}.rb")
9
+ template "view_test.rb.erb", File.join("test/views", class_path, "#{file_name}_test.rb")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ say "Installing Phlex..."
4
+
5
+ application_configuration_path = Rails.root.join("config/application.rb")
6
+ application_configuration_content = File.read(application_configuration_path)
7
+
8
+ pattern = %r(config.autoload_paths << (Rails.root.join\(.app.\)|.\#{root}/app.)\n)
9
+
10
+ unless application_configuration_content.match?(pattern)
11
+ inject_into_class(
12
+ application_configuration_path,
13
+ "Application",
14
+ %( config.autoload_paths << "\#{root}/app"\n)
15
+ )
16
+ end
17
+
18
+ unless Rails.root.join("app/views/application_view.rb").exist?
19
+ create_file(Rails.root.join("app/views/application_view.rb"), <<~RUBY)
20
+ # frozen_string_literal: true
21
+
22
+ module Views
23
+ module ApplicationView
24
+ include Rails.application.routes.url_helpers
25
+ end
26
+ end
27
+ RUBY
28
+ end
29
+
30
+ tailwind_config_path = Rails.root.join("config/tailwind.config.js")
31
+
32
+ if tailwind_config_path.exist?
33
+ insert_into_file tailwind_config_path, after: "content: [" do
34
+ "\n './app/views/**/*.rb',"
35
+ end
36
+ end
37
+
38
+ say "Phlex successfully installed!"
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Rails
5
+ # A wrapper for Rails’ OutputBuffer that acts like a Phlex buffer.
6
+
7
+ class Buffer < SimpleDelegator
8
+ def <<(value)
9
+ __getobj__.safe_append = (value)
10
+ self
11
+ end
12
+
13
+ def length
14
+ __getobj__.length
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/engine"
4
+
5
+ module Phlex
6
+ module Rails
7
+ class Engine < ::Rails::Engine
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Rails
5
+ class Form < Phlex::HTML
6
+ def initialize(model)
7
+ @model = model
8
+ end
9
+
10
+ def self.input_field(method_name, type:)
11
+ define_method method_name do |field, value: @model.attributes[field.to_s], **attributes|
12
+ input(
13
+ name: field_name(field),
14
+ type: type,
15
+ value: value,
16
+ **attributes
17
+ )
18
+ end
19
+ end
20
+
21
+ def template(&block)
22
+ form action: @url, method: @method do
23
+ authenticity_token_field
24
+ yield_content(&block)
25
+ end
26
+ end
27
+
28
+ def authenticity_token_field
29
+ input(
30
+ name: "authenticity_token",
31
+ type: "hidden",
32
+ value: @_view_context.form_authenticity_token
33
+ )
34
+ end
35
+
36
+ def submit(value)
37
+ input(
38
+ name: "commit",
39
+ type: "submit",
40
+ value: value
41
+ )
42
+ end
43
+
44
+ def url
45
+ @_view_context.url_for(@model)
46
+ end
47
+
48
+ def field_name(*field)
49
+ @_view_context.field_name(ActiveModel::Naming.param_key(@model.class), *field)
50
+ end
51
+
52
+ input_field :url_field, type: "url"
53
+ input_field :text_field, type: "text"
54
+ input_field :date_field, type: "date"
55
+ input_field :time_field, type: "time"
56
+ input_field :week_field, type: "week"
57
+ input_field :month_field, type: "month"
58
+ input_field :email_field, type: "email"
59
+ input_field :color_field, type: "color"
60
+ input_field :hidden_field, type: "hidden"
61
+ input_field :search_field, type: "search"
62
+ input_field :password_field, type: "password"
63
+ input_field :telephone_field, type: "tel"
64
+ input_field :datetime_local_field, type: "datetime-local"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Rails
5
+ module Helpers
6
+ module CSPMetaTag
7
+ def csp_meta_tag(**options)
8
+ if (output = @_view_context.csp_meta_tag(**options))
9
+ @_target << output
10
+ end
11
+ end
12
+ end
13
+
14
+ module CSRFMetaTags
15
+ def csrf_meta_tags
16
+ if (output = @_view_context.csrf_meta_tags)
17
+ @_target << output
18
+ end
19
+ end
20
+ end
21
+
22
+ module ActionCableMetaTag
23
+ def action_cable_meta_tag
24
+ if (output = @_view_context.action_cable_meta_tag)
25
+ @_target << output
26
+ end
27
+ end
28
+ end
29
+
30
+ module FormWith
31
+ class BufferedFormWith < Phlex::Buffered
32
+ alias_method :check_box, :__output_method__
33
+ alias_method :collection_check_boxes, :__output_method__
34
+ alias_method :collection_radio_buttons, :__output_method__
35
+ alias_method :collection_select, :__output_method__
36
+ alias_method :color_field, :__output_method__
37
+ alias_method :date_field, :__output_method__
38
+ alias_method :date_select, :__output_method__
39
+ alias_method :datetime_field, :__output_method__
40
+ alias_method :datetime_local_field, :__output_method__
41
+ alias_method :datetime_select, :__output_method__
42
+ alias_method :email_field, :__output_method__
43
+ alias_method :file_field, :__output_method__
44
+ alias_method :grouped_collection_select, :__output_method__
45
+ alias_method :hidden_field, :__output_method__
46
+ alias_method :label, :__output_method__
47
+ alias_method :month_field, :__output_method__
48
+ alias_method :number_field, :__output_method__
49
+ alias_method :password_field, :__output_method__
50
+ alias_method :phone_field, :__output_method__
51
+ alias_method :radio_button, :__output_method__
52
+ alias_method :range_field, :__output_method__
53
+ alias_method :search_field, :__output_method__
54
+ alias_method :select, :__output_method__
55
+ alias_method :submit, :__output_method__
56
+ alias_method :telephone_field, :__output_method__
57
+ alias_method :text_area, :__output_method__
58
+ alias_method :text_field, :__output_method__
59
+ alias_method :time_field, :__output_method__
60
+ alias_method :time_select, :__output_method__
61
+ alias_method :time_zone_select, :__output_method__
62
+ alias_method :url_field, :__output_method__
63
+ alias_method :week_field, :__output_method__
64
+ alias_method :weekday_select, :__output_method__
65
+ alias_method :button, :__output_method__
66
+ end
67
+
68
+ def form_with(*args, **kwargs, &block)
69
+ @_target << @_view_context.form_with(*args, **kwargs) { |form|
70
+ capture do
71
+ yield(
72
+ BufferedFormWith.new(form, buffer: @_target)
73
+ )
74
+ end.html_safe
75
+ }
76
+ end
77
+ end
78
+
79
+ module StylesheetLinkTag
80
+ def stylesheet_link_tag(*sources)
81
+ if (output = @_view_context.stylesheet_link_tag(*sources))
82
+ @_target << output
83
+ end
84
+ end
85
+ end
86
+
87
+ module FaviconLinkTag
88
+ def favicon_link_tag(*args)
89
+ if (output = @_view_context.favicon_link_tag(*args))
90
+ @_target << output
91
+ end
92
+ end
93
+ end
94
+
95
+ module PreloadLinkTag
96
+ def preload_link_tag(*args)
97
+ if (output = @_view_context.preload_link_tag(*args))
98
+ @_target << output
99
+ end
100
+ end
101
+ end
102
+
103
+ module JavaScriptIncludeTag
104
+ def javascript_include_tag(*sources)
105
+ if (output = @_view_context.javascript_include_tag(*sources))
106
+ @_target << output
107
+ end
108
+ end
109
+ end
110
+
111
+ module JavaScriptImportmapTags
112
+ def javascript_importmap_tags
113
+ if (output = @_view_context.javascript_importmap_tags)
114
+ @_target << output
115
+ end
116
+ end
117
+ end
118
+
119
+ module ContentFor
120
+ def content_for(slot, &block)
121
+ @_view_context.content_for(slot, capture(&block))
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Rails
5
+ module Layout
6
+ include Helpers::CSPMetaTag
7
+ include Helpers::CSRFMetaTags
8
+ include Helpers::FaviconLinkTag
9
+ include Helpers::PreloadLinkTag
10
+ include Helpers::StylesheetLinkTag
11
+ include Helpers::ActionCableMetaTag
12
+ include Helpers::JavaScriptIncludeTag
13
+ include Helpers::JavaScriptImportmapTags
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module Rails
5
+ module Renderable
6
+ def helpers
7
+ @_view_context
8
+ end
9
+
10
+ def render(renderable, *args, **kwargs, &block)
11
+ return super if renderable.is_a?(Phlex::HTML)
12
+ return super if renderable.is_a?(Class) && renderable < Phlex::HTML
13
+
14
+ @_target << @_view_context.render(renderable, *args, **kwargs, &block)
15
+
16
+ nil
17
+ end
18
+
19
+ def render_in(view_context, &block)
20
+ output_buffer = view_context.output_buffer
21
+
22
+ # Since https://github.com/rails/rails/pull/45731
23
+ if output_buffer.respond_to?(:raw_buffer)
24
+ buffer = output_buffer.raw_buffer
25
+ else
26
+ buffer = Buffer.new(output_buffer)
27
+ end
28
+
29
+ call(buffer, view_context: view_context, &block)
30
+
31
+ nil
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex
4
- module Rails
5
- VERSION = "0.1.0"
6
- end
4
+ module Rails
5
+ VERSION = "0.2.0"
6
+ end
7
7
  end
data/lib/phlex/rails.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "rails/version"
3
+ require "phlex"
4
+ require "phlex/rails/engine"
4
5
 
5
- module Phlex
6
- module Rails
7
- class Error < StandardError; end
8
- # Your code goes here...
9
- end
6
+ module Phlex::Rails
7
+ Loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false).tap do |loader|
8
+ loader.push_dir("#{__dir__}/rails", namespace: Phlex::Rails)
9
+ loader.setup
10
+ end
11
+
12
+ Phlex::HTML.include(Phlex::Rails::Renderable)
10
13
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "phlex/testing/view_helper"
4
+
5
+ module Phlex::Testing
6
+ module Rails
7
+ module ViewHelper
8
+ include Phlex::Testing::ViewHelper
9
+
10
+ def view_context
11
+ controller.view_context
12
+ end
13
+
14
+ def controller
15
+ @controller ||= ActionView::TestCase::TestController.new
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "phlex/rails"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :phlex do
4
+ desc "Install Phlex in the Rails application"
5
+
6
+ task :install do
7
+ install_file_path = File.expand_path("../install/phlex.rb", __dir__)
8
+
9
+ system "#{RbConfig.ruby} bin/rails app:template LOCATION=#{install_file_path}"
10
+ end
11
+ end
data/phlex_logo.png ADDED
Binary file