phlex-rails 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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