diff --git a/.github/workflows/ruby_on_rails.yml b/.github/workflows/ruby_on_rails.yml index a40908be6..360843e1d 100644 --- a/.github/workflows/ruby_on_rails.yml +++ b/.github/workflows/ruby_on_rails.yml @@ -22,6 +22,7 @@ jobs: ruby-version: ${{ matrix.ruby_version }} - name: Build and test with Rake run: | + sudo apt-get install libsqlite3-dev gem install bundler:1.14.0 bundle update bundle install --jobs 4 --retry 3 diff --git a/.gitignore b/.gitignore index cc6dfae43..70eeeab0b 100644 --- a/.gitignore +++ b/.gitignore @@ -47,5 +47,7 @@ build-iPhoneSimulator/ # .ruby-version # .ruby-gemset +/**/*.sqlite3 + # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc diff --git a/Gemfile b/Gemfile index 1123388c7..d96d85cf0 100644 --- a/Gemfile +++ b/Gemfile @@ -6,3 +6,4 @@ gemspec rails_version = "#{ENV['RAILS_VERSION'] || '6.0.0'}" gem "rails", rails_version == "master" ? { github: "rails/rails" } : rails_version +gem "sqlite3", rails_version >= "6.0.0" ? ">= 1.4.0" : "< 1.4.0" diff --git a/Gemfile.lock b/Gemfile.lock index 812c6a719..3c66e644b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -146,6 +146,7 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + sqlite3 (1.4.1) temple (0.8.1) thor (0.20.3) thread_safe (0.3.6) @@ -171,6 +172,7 @@ DEPENDENCIES rubocop (= 0.74) rubocop-github (~> 0.13.0) slim (~> 4.0) + sqlite3 (>= 1.4.0) BUNDLED WITH 1.17.3 diff --git a/README.md b/README.md index 862555b99..8332aa35c 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,24 @@ Components can be rendered via: `render(component: TestComponent, locals: { foo: :bar })` +**Rendering components through models** + +Passing model instances will cause `render` to look for its respective component class. + +The component is instantiated with the rendered model instance. + +Example for a `Post` model: + +`render(@post)` + +```ruby +class PostComponent < ActionView::Component + def initialize(post) + @post = post + end +end +``` + The following syntax has been deprecated and will be removed in v2.0.0: `render(TestComponent.new(foo: :bar))` diff --git a/lib/action_view/component.rb b/lib/action_view/component.rb index b750163e5..e51e25b5a 100644 --- a/lib/action_view/component.rb +++ b/lib/action_view/component.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true require "action_view/component/monkey_patch" +require "action_view/component/active_model_conversion_monkey_patch" require "action_view/component/base" require "action_view/component/railtie" diff --git a/lib/action_view/component/active_model_conversion_monkey_patch.rb b/lib/action_view/component/active_model_conversion_monkey_patch.rb new file mode 100644 index 000000000..dd84192a7 --- /dev/null +++ b/lib/action_view/component/active_model_conversion_monkey_patch.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module ActiveModel + module Conversion + def to_component_class + "#{self.class.name}Component".safe_constantize + end + end +end diff --git a/lib/action_view/component/monkey_patch.rb b/lib/action_view/component/monkey_patch.rb index 8e213c9db..41460b372 100644 --- a/lib/action_view/component/monkey_patch.rb +++ b/lib/action_view/component/monkey_patch.rb @@ -17,6 +17,8 @@ def render(options = {}, args = {}, &block) options.new(args).render_in(self, &block) elsif options.is_a?(Hash) && options.has_key?(:component) options[:component].new(options[:locals]).render_in(self, &block) + elsif options.respond_to?(:to_component_class) && !options.to_component_class.nil? + options.to_component_class.new(options).render_in(self, &block) else super end diff --git a/test/action_view/component_test.rb b/test/action_view/component_test.rb index a9bf38ce6..c5ac22707 100644 --- a/test/action_view/component_test.rb +++ b/test/action_view/component_test.rb @@ -251,6 +251,13 @@ def test_renders_component_with_rb_in_its_name assert_equal "Editorb!\n", render_inline(EditorbComponent).text end + def test_to_component_class + post = Post.new(title: "Awesome post") + + assert_equal PostComponent, post.to_component_class + assert_equal "The Awesome post component!", render_inline(post).first.to_html + end + private def modify_file(file, content) diff --git a/test/app/components/post_component.html.erb b/test/app/components/post_component.html.erb new file mode 100644 index 000000000..5cb65df70 --- /dev/null +++ b/test/app/components/post_component.html.erb @@ -0,0 +1 @@ +The <%= @post.title %> component! diff --git a/test/app/components/post_component.rb b/test/app/components/post_component.rb new file mode 100644 index 000000000..2d87e4b02 --- /dev/null +++ b/test/app/components/post_component.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class PostComponent < ActionView::Component::Base + def initialize(post) + @post = post + end +end diff --git a/test/app/models/application_record.rb b/test/app/models/application_record.rb new file mode 100644 index 000000000..71fbba5b3 --- /dev/null +++ b/test/app/models/application_record.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/test/app/models/post.rb b/test/app/models/post.rb new file mode 100644 index 000000000..a9c55bd6f --- /dev/null +++ b/test/app/models/post.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class Post < ApplicationRecord +end diff --git a/test/config/application.rb b/test/config/application.rb index ce4a1906a..9279698b0 100644 --- a/test/config/application.rb +++ b/test/config/application.rb @@ -2,6 +2,7 @@ require File.expand_path("../boot", __FILE__) +require "active_record/railtie" require "active_model/railtie" require "action_controller/railtie" require "action_view/railtie" diff --git a/test/config/database.yml b/test/config/database.yml new file mode 100644 index 000000000..febf936bc --- /dev/null +++ b/test/config/database.yml @@ -0,0 +1,8 @@ +default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +test: + <<: *default + database: db/test.sqlite3 diff --git a/test/test_helper.rb b/test/test_helper.rb index 76a2127fe..11567425e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -16,3 +16,11 @@ def trim_result(html) html.delete(" \t\r\n") end + +ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") + +ActiveRecord::Schema.define do + create_table :posts, force: true do |t| + t.string :title + end +end