nice_partials 0.1.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,25 +1,12 @@
1
1
  require_relative "partial"
2
2
 
3
3
  module NicePartials::Helper
4
- def np
5
- NicePartials::Partial.new(self)
6
- end
7
-
8
- def nice_partials_push_t_prefix(prefix)
9
- @_nice_partials_t_prefixes ||= []
10
- @_nice_partials_t_prefixes << prefix
4
+ def nice_partial_with(local_assigns)
5
+ NicePartials::Partial.new(self, local_assigns)
11
6
  end
12
7
 
13
- def nice_partials_pop_t_prefix
14
- @_nice_partials_t_prefixes ||= []
15
- @_nice_partials_t_prefixes.pop
16
- end
17
-
18
- def t(key, options = {})
19
- if @_nice_partials_t_prefixes&.any? && key.first == '.'
20
- key = "#{@_nice_partials_t_prefixes.last}#{key}"
21
- end
22
-
23
- super(key, **options)
8
+ def nice_partial
9
+ NicePartials::Partial.new(self)
24
10
  end
11
+ alias_method :np, :nice_partial
25
12
  end
@@ -1,59 +1,121 @@
1
1
  # Monkey patch required to make `t` work as expected. Is this evil?
2
2
  # TODO Do we need to monkey patch other types of renderers as well?
3
- class ActionView::PartialRenderer
4
- alias_method :original_render, :render
5
-
6
- # See `content_for` in `lib/nice_partials/partial.rb` for something similar.
7
- def render(partial, context, block)
8
- if block
9
- partial_prefix = nice_partials_locale_prefix_from_view_context_and_block(context, block)
10
- context.nice_partials_push_t_prefix partial_prefix
11
- else
12
- # Render partial calls with no block should disable any prefix magic.
13
- context.nice_partials_push_t_prefix ''
3
+ module NicePartials::RenderingWithLocalePrefix
4
+ ActionView::Base.prepend self
5
+
6
+ def capture(*, &block)
7
+ with_nice_partials_t_prefix(lookup_context, block) { super }
8
+ end
9
+
10
+ def t(key, options = {})
11
+ if (prefix = @_nice_partials_t_prefix) && key.first == '.'
12
+ key = "#{prefix}#{key}"
14
13
  end
15
14
 
16
- begin
17
- result = original_render(partial, context, block)
18
- rescue Exception => exception
19
- # If there was some sort of exception thrown, we also need to pop the `t` prefix.
20
- # This provides compatibility with other libraries that depend on catching exceptions from the view renderer.
21
- context.nice_partials_pop_t_prefix
22
- raise exception
15
+ super(key, **options)
16
+ end
17
+
18
+ private
19
+
20
+ def with_nice_partials_t_prefix(lookup_context, block)
21
+ _nice_partials_t_prefix = @_nice_partials_t_prefix
22
+ @_nice_partials_t_prefix = block ? NicePartials.locale_prefix_from(lookup_context, block) : nil
23
+ yield
24
+ ensure
25
+ @_nice_partials_t_prefix = _nice_partials_t_prefix
26
+ end
27
+ end
28
+
29
+ require "active_support/deprecation"
30
+ NicePartials::DEPRECATOR = ActiveSupport::Deprecation.new("1.0", "nice_partials")
31
+
32
+ module NicePartials::RenderingWithAutoContext
33
+ ActionView::Base.prepend self
34
+
35
+ def __partials
36
+ @__partials ||= NicePartials::Partial::Stack.new
37
+ end
38
+ delegate :partial, to: :__partials
39
+
40
+ def p(*args)
41
+ if args.empty?
42
+ NicePartials::DEPRECATOR.deprecation_warning :p, :partial # In-branch printing so we don't warn on legit `Kernel.p` calls.
43
+ partial
44
+ else
45
+ super # …we're really Kernel.p
23
46
  end
47
+ end
24
48
 
25
- # Whether there was a block or not, pop off whatever we put on the stack.
26
- context.nice_partials_pop_t_prefix
49
+ # Overrides `ActionView::Helpers::RenderingHelper#render` to push a new `nice_partial`
50
+ # on the stack, so rendering has a fresh `partial` to store content in.
51
+ def render(options = {}, locals = {}, &block)
52
+ partial_locals = options.is_a?(Hash) ? options[:locals] : locals
53
+ __partials.prepend nice_partial_with(partial_locals)
54
+ super
55
+ ensure
56
+ __partials.shift
57
+ end
27
58
 
28
- return result
59
+ # Since Action View passes any `yield`s in partials through `_layout_for`, we
60
+ # override `_layout_for` to detects if it's a capturing yield and append the
61
+ # current partial to the arguments.
62
+ #
63
+ # So `render … do |some_object|` can become `render … do |some_object, partial|`
64
+ # without needing to find and update the inner `yield some_object` call.
65
+ def _layout_for(*arguments, &block)
66
+ if block && !arguments.first.is_a?(Symbol)
67
+ capture_with_outer_partial_access(*arguments, &block)
68
+ else
69
+ super
70
+ end
29
71
  end
30
72
 
31
- # This and ActionView::Template#render below are for compatibility
32
- # with Ruby 3, as opposed to altering the original functionality.
73
+ # Reverts `partial` to return the outer partial before the `render` call started.
74
+ #
75
+ # So we don't clobber the `partial` shown here:
76
+ #
77
+ # <%= render "card" do |inner_partial| %>
78
+ # <% inner_partial.content_for :title, partial.content_for(:title) %>
79
+ # <% end %>
80
+ #
81
+ # Note: this happens because the `@partial` instance variable is shared between all
82
+ # `render` calls since rendering happens in one `ActionView::Base` instance.
83
+ def capture_with_outer_partial_access(*arguments, &block)
84
+ __partials.locate_previous
85
+ __partials.first.capture(*arguments, &block)
86
+ ensure
87
+ __partials.reset_locator
88
+ end
89
+ end
90
+
91
+ module NicePartials::PartialRendering
92
+ ActionView::PartialRenderer.prepend self
93
+
94
+ # Automatically captures the `block` in case the partial has no manual capturing `yield` call.
95
+ #
96
+ # This manual equivalent would be inserting this:
97
+ #
98
+ # <% yield partial %>
33
99
  def render_partial_template(view, locals, template, layout, block)
34
- ActiveSupport::Notifications.instrument(
35
- "render_partial.action_view",
36
- identifier: template.identifier,
37
- layout: layout && layout.virtual_path
38
- ) do |payload|
39
- content = template.render(view, locals, ActionView::OutputBuffer.new, {add_to_stack: !block}) do |*name|
40
- view._layout_for(*name, &block)
41
- end
42
-
43
- content = layout.render(view, locals) { content } if layout
44
- payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
45
- build_rendered_template(content, template)
46
- end
100
+ view.capture_with_outer_partial_access(&block) if block && !template.has_capturing_yield?
101
+ super
47
102
  end
48
103
  end
49
104
 
50
- class ActionView::Template
51
- def render(view, locals, buffer = ActionView::OutputBuffer.new, flag = {add_to_stack: true}, &block)
52
- instrument_render_template do
53
- compile!(view)
54
- view._run(method_name, self, locals, buffer, **flag, &block)
55
- end
56
- rescue => e
57
- handle_render_error(view, e)
105
+ module NicePartials::CapturingYieldDetection
106
+ ActionView::Template.include self
107
+
108
+ # Matches yields that'll end up calling `capture`:
109
+ # <%= yield %>
110
+ # <%= yield something_else %>
111
+ #
112
+ # Doesn't match obfuscated `content_for` invocations, nor custom yields:
113
+ # <%= yield :message %>
114
+ # <%= something.yield %>
115
+ #
116
+ # Note: `<%= yield %>` becomes `yield :layout` with no `render` `block`, though this method assumes a block is passed.
117
+ def has_capturing_yield?
118
+ defined?(@has_capturing_yield) ? @has_capturing_yield :
119
+ @has_capturing_yield = source.match?(/[^\.\b]yield[\(? ]+(%>|[^:])/)
58
120
  end
59
121
  end
@@ -0,0 +1,54 @@
1
+ class NicePartials::Partial::Content
2
+ class Options < Hash
3
+ def initialize(view_context)
4
+ @view_context = view_context
5
+ end
6
+
7
+ def to_s
8
+ @view_context.tag.attributes(self)
9
+ end
10
+ end
11
+
12
+ def initialize(view_context, content = nil)
13
+ @view_context, @options = view_context, Options.new(view_context)
14
+ @content = ActiveSupport::SafeBuffer.new and concat content
15
+ end
16
+ delegate :to_s, :present?, to: :@content
17
+
18
+ # Contains options passed to a partial:
19
+ #
20
+ # <% partial.title class: "post-title" %> # partial.title.options # => { class: "post-title" }
21
+ #
22
+ # # Automatically runs `tag.attributes` when `to_s` is called, e.g.:
23
+ # <h1 <% partial.title.options %>> # => <h1 class="post-title">
24
+ attr_reader :options
25
+
26
+ def write?(content = nil, **new_options, &block)
27
+ @options.merge! new_options
28
+ append content or capture block
29
+ end
30
+
31
+ def write(...)
32
+ write?(...)
33
+ self
34
+ end
35
+
36
+ private
37
+
38
+ def append(content)
39
+ case
40
+ when content.respond_to?(:render_in) then concat content.render_in(@view_context)
41
+ when content.respond_to?(:call) then capture content
42
+ else
43
+ concat content
44
+ end
45
+ end
46
+
47
+ def capture(block)
48
+ append @view_context.capture(&block) if block
49
+ end
50
+
51
+ def concat(string)
52
+ @content << string.to_s if string.present?
53
+ end
54
+ end
@@ -0,0 +1,48 @@
1
+ class NicePartials::Partial::Section < NicePartials::Partial::Content
2
+ def yield(*arguments)
3
+ chunks.each { append @view_context.capture(*arguments, &_1) }
4
+ self
5
+ end
6
+
7
+ def present?
8
+ chunks.present? || super
9
+ end
10
+
11
+ undef_method :p # Remove Kernel.p here to pipe through method_missing and hit tag proxy.
12
+
13
+ # Implements our proxying to the `@view_context` or `@view_context.tag`.
14
+ #
15
+ # `@view_context` proxying forwards the message and automatically appends any content, so you can do things like:
16
+ #
17
+ # <% partial.body.render "form", tangible_thing: @tangible_thing %>
18
+ # <% partial.body.link_to @document.name, @document %>
19
+ # <% partial.body.t ".body" %>
20
+ #
21
+ # `@view_context.tag` proxy lets you build bespoke elements based on content and options provided:
22
+ #
23
+ # <% partial.title "Some title content", class: "xl" %> # Write the content and options to the `title`
24
+ # <% partial.title.h2 ", appended" %> # => <h2 class="xl">Some title content, appended</h2>
25
+ #
26
+ # Note that NicePartials don't support deep merging attributes out of the box.
27
+ # For that, bundle https://github.com/seanpdoyle/attributes_and_token_lists
28
+ def method_missing(meth, *arguments, **keywords, &block)
29
+ if meth != :p && @view_context.respond_to?(meth)
30
+ append @view_context.public_send(meth, *arguments, **keywords, &block)
31
+ else
32
+ @view_context.tag.public_send(meth, @content + arguments.first.to_s, **options.merge(keywords), &block)
33
+ end
34
+ end
35
+ def respond_to_missing?(...) = @view_context.respond_to?(...)
36
+
37
+ private
38
+
39
+ def capture(block)
40
+ if block&.arity&.nonzero?
41
+ chunks << block
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def chunks() = @chunks ||= []
48
+ end
@@ -0,0 +1,19 @@
1
+ class NicePartials::Partial::Stack
2
+ def initialize
3
+ @partials = []
4
+ reset_locator
5
+ end
6
+ delegate :prepend, :shift, :first, to: :@partials
7
+
8
+ def partial
9
+ @partials.public_send @locator
10
+ end
11
+
12
+ def locate_previous
13
+ @locator = :second
14
+ end
15
+
16
+ def reset_locator
17
+ @locator = :first
18
+ end
19
+ end
@@ -1,39 +1,111 @@
1
1
  module NicePartials
2
2
  class Partial
3
+ autoload :Content, "nice_partials/partial/content"
4
+ autoload :Section, "nice_partials/partial/section"
5
+ autoload :Stack, "nice_partials/partial/stack"
6
+
3
7
  delegate_missing_to :@view_context
4
8
 
5
- def initialize(view_context)
6
- @view_context = view_context
7
- @key = SecureRandom.uuid
9
+ def initialize(view_context, local_assigns = nil)
10
+ @view_context, @local_assigns = view_context, local_assigns
8
11
  end
9
12
 
10
- def yield(name = nil)
11
- raise "You can only use Nice Partial's yield method to retrieve the content of named content area blocks. If you're not trying to fetch the content of a named content area block, you don't need Nice Partials! You can just call the vanilla Rails `yield`." unless name
12
- content_for(name)
13
+ def yield(*arguments, &block)
14
+ if arguments.empty?
15
+ @captured_buffer
16
+ else
17
+ content_for(*arguments, &block)
18
+ end
13
19
  end
14
20
 
15
21
  def helpers(&block)
16
- class_eval &block
22
+ self.class.class_eval(&block)
17
23
  end
18
24
 
19
- # See the `ActionView::PartialRenderer` monkey patch in `lib/nice_partials/monkey_patch.rb` for something similar.
20
- def content_for(name, content = nil, options = {}, &block)
21
- if block_given?
22
- partial_prefix = nice_partials_locale_prefix_from_view_context_and_block(@view_context, block)
23
- @view_context.nice_partials_push_t_prefix(partial_prefix)
25
+ # `translate` is a shorthand to set `content_for` with content that's run through
26
+ # the view's `translate`/`t` context.
27
+ #
28
+ # partial.t :title # => partial.content_for :title, t(".title")
29
+ # partial.t title: :section # => partial.content_for :title, t(".section")
30
+ # partial.t title: "some.custom.key" # => partial.content_for :title, t("some.custom.key")
31
+ # partial.t :description, title: :header # Mixing is supported too.
32
+ #
33
+ # Note that `partial.t "some.custom.key"` can't derive a `content_for` name, so an explicit
34
+ # name must be provided e.g. `partial.t title: "some.custom.key"`.
35
+ def translate(*names, **renames)
36
+ names.chain(renames).each do |name, key = name|
37
+ content_for name, @view_context.t(key.is_a?(String) ? key : ".#{key}")
24
38
  end
39
+ end
40
+ alias t translate
25
41
 
26
- result = @view_context.content_for("#{name}_#{@key}".to_sym, content, options, &block)
42
+ # Allows an inner partial to copy content from an outer partial.
43
+ #
44
+ # Additionally a hash of keys to rename in the new partial context can be passed.
45
+ #
46
+ # First, an outer partial gets some content set:
47
+ # <% partial.title "Hello there" %>
48
+ # <% partial.byline "Somebody" %>
49
+ #
50
+ # Second, a new partial is rendered, but we want to extract the title, byline content but rename the byline key too:
51
+ # <%= render "shared/title" do |cp| %>
52
+ # <% cp.content_from partial, :title, byline: :name %>
53
+ # <% end %>
54
+ #
55
+ # # Third, the contents with any renames are accessible in shared/_title.html.erb:
56
+ # <%= partial.title %> # => "Hello there"
57
+ # <%= partial.name %> # => "Somebody"
58
+ def content_from(partial, *names, **renames)
59
+ names.chain(renames).each { |key, new_key = key| public_send new_key, partial.public_send(key).to_s }
60
+ end
27
61
 
28
- if block_given?
29
- @view_context.nice_partials_pop_t_prefix
30
- end
62
+ # Similar to Rails' built-in `content_for` except it defers any block execution
63
+ # and lets you pass arguments into it, like so:
64
+ #
65
+ # # Here we store a block with some eventual content…
66
+ # <% partial.title { |tag| tag.h1 } %>
67
+ #
68
+ # # …which we can then yield into with some predefined options later.
69
+ # <%= partial.title.yield tag.with_options(class: "text-bold") %>
70
+ def section(name, content = nil, **options, &block)
71
+ section_from(name).then { _1.write?(content, **options, &block) ? nil : _1 }
72
+ end
73
+
74
+ def section?(name)
75
+ @sections&.dig(name).present?
76
+ end
77
+ alias content_for? section?
78
+
79
+ def content_for(...)
80
+ section(...)&.to_s
81
+ end
82
+
83
+ def slice(*keys)
84
+ keys.index_with { content_for _1 }
85
+ end
86
+
87
+ def capture(*arguments, &block)
88
+ @captured_buffer = @view_context.capture(*arguments, self, &block)
89
+ end
31
90
 
32
- return result
91
+ private
92
+
93
+ def section_from(name)
94
+ @sections ||= {} and @sections[name] ||= Section.new(@view_context, @local_assigns&.dig(name))
95
+ end
96
+
97
+ def method_missing(meth, *arguments, **keywords, &block)
98
+ if @view_context.respond_to?(meth)
99
+ @view_context.public_send(meth, *arguments, **keywords, &block)
100
+ else
101
+ define_accessor meth and public_send(meth, *arguments, **keywords, &block)
102
+ end
33
103
  end
34
104
 
35
- def content_for?(name)
36
- @view_context.content_for?("#{name}_#{@key}".to_sym)
105
+ def define_accessor(name)
106
+ name = name.to_s.chomp("?").to_sym
107
+ self.class.define_method(name) { |content = nil, **options, &block| section(name, content, **options, &block) }
108
+ self.class.define_method("#{name}?") { section?(name) }
37
109
  end
38
110
  end
39
111
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NicePartials
4
- VERSION = "0.1.6"
4
+ VERSION = "0.9.0"
5
5
  end
data/lib/nice_partials.rb CHANGED
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "nice_partials/version"
4
- require_relative "nice_partials/helper"
5
- require_relative "nice_partials/monkey_patch"
6
4
 
7
5
  module NicePartials
8
- end
9
-
10
- # TODO Is there somewhere better we can put this?
11
- def nice_partials_locale_prefix_from_view_context_and_block(context, block)
12
- root_paths = context.view_renderer.lookup_context.view_paths.map(&:path)
13
- partial_location = block.source_location.first.dup
14
- root_paths.each { |path| partial_location.gsub!(/^#{path}\//, '') }
15
- partial_location.split('.').first.gsub('/_', '/').gsub('/', '.')
6
+ def self.locale_prefix_from(lookup_context, block)
7
+ partial_location = block.source_location.first.dup
8
+ lookup_context.view_paths.each { partial_location.delete_prefix!(_1.path)&.delete_prefix!("/") }
9
+ partial_location.split('.').first.gsub('/_', '/').gsub('/', '.')
10
+ end
16
11
  end
17
12
 
18
13
  ActiveSupport.on_load :action_view do
14
+ require_relative "nice_partials/monkey_patch"
15
+
16
+ require_relative "nice_partials/helper"
19
17
  include NicePartials::Helper
20
18
  end
@@ -12,9 +12,15 @@ Gem::Specification.new do |gem|
12
12
  gem.homepage = "https://github.com/bullet-train-co/nice_partials"
13
13
  gem.license = "MIT"
14
14
 
15
- gem.files = Dir["{**/}{.*,*}"].select{ |path| File.file?(path) && path !~ /^pkg/ }
16
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ # Specify which files should be added to the gem when it is released.
16
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
17
+ gem.files = Dir.chdir(__dir__) do
18
+ `git ls-files -z`.split("\x0").reject do |f|
19
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
20
+ end
21
+ end
22
+ gem.bindir = "exe"
23
+ gem.executables = gem.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
18
24
  gem.require_paths = ["lib"]
19
25
 
20
26
  gem.required_ruby_version = ">= 2.0"
@@ -22,4 +28,5 @@ Gem::Specification.new do |gem|
22
28
  gem.add_dependency "actionview", '>= 4.2.6'
23
29
 
24
30
  gem.add_development_dependency "rails"
31
+ gem.add_development_dependency "standard"
25
32
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nice_partials
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
8
8
  - Dom Christie
9
9
  autorequire:
10
- bindir: bin
10
+ bindir: exe
11
11
  cert_chain: []
12
- date: 2022-02-10 00:00:00.000000000 Z
12
+ date: 2023-01-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionview
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: standard
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  description: A little bit of magic to make partials perfect for components.
43
57
  email:
44
58
  - andrew.culver@gmail.com
@@ -47,8 +61,6 @@ executables: []
47
61
  extensions: []
48
62
  extra_rdoc_files: []
49
63
  files:
50
- - ".gitignore"
51
- - ".travis.yml"
52
64
  - CHANGELOG.md
53
65
  - CODE_OF_CONDUCT.md
54
66
  - Gemfile
@@ -59,13 +71,11 @@ files:
59
71
  - lib/nice_partials/helper.rb
60
72
  - lib/nice_partials/monkey_patch.rb
61
73
  - lib/nice_partials/partial.rb
74
+ - lib/nice_partials/partial/content.rb
75
+ - lib/nice_partials/partial/section.rb
76
+ - lib/nice_partials/partial/stack.rb
62
77
  - lib/nice_partials/version.rb
63
78
  - nice_partials.gemspec
64
- - test/fixtures/_basic.html.erb
65
- - test/fixtures/_card.html.erb
66
- - test/fixtures/card_test.html.erb
67
- - test/renderer_test.rb
68
- - test/test_helper.rb
69
79
  homepage: https://github.com/bullet-train-co/nice_partials
70
80
  licenses:
71
81
  - MIT
@@ -85,13 +95,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
95
  - !ruby/object:Gem::Version
86
96
  version: '0'
87
97
  requirements: []
88
- rubygems_version: 3.2.22
98
+ rubygems_version: 3.4.1
89
99
  signing_key:
90
100
  specification_version: 4
91
101
  summary: A little bit of magic to make partials perfect for components.
92
- test_files:
93
- - test/fixtures/_basic.html.erb
94
- - test/fixtures/_card.html.erb
95
- - test/fixtures/card_test.html.erb
96
- - test/renderer_test.rb
97
- - test/test_helper.rb
102
+ test_files: []
data/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- Gemfile.lock
2
- /pkg
3
- *~
data/.travis.yml DELETED
@@ -1,18 +0,0 @@
1
- sudo: false
2
- language: ruby
3
-
4
- rvm:
5
- - 3.0
6
- - 2.7
7
- - 2.6
8
- - 2.5
9
- - 2.4
10
- - ruby-head
11
- - jruby-9.2.11.1
12
- - truffleruby-20.2.0
13
-
14
- matrix:
15
- allow_failures:
16
- - rvm: 2.4
17
- - rvm: ruby-head
18
- # fast_finish: true
@@ -1,2 +0,0 @@
1
- <% yield p = np %>
2
- <%= p.yield :message %>
@@ -1,13 +0,0 @@
1
- <% yield p = np %>
2
-
3
- <div class="card">
4
- <%= p.yield :image %>
5
- <div class="card-body">
6
- <h5 class="card-title"><%= title %></h5>
7
- <% if p.content_for? :body %>
8
- <p class="card-text">
9
- <%= p.content_for :body %>
10
- </p>
11
- <% end %>
12
- </div>
13
- </div>
@@ -1,9 +0,0 @@
1
- <%= render "card", title: "Some Title" do |p| %>
2
- <% p.content_for :body do %>
3
- Lorem Ipsum
4
- <% end %>
5
-
6
- <% p.content_for :image do %>
7
- <img src="https://example.com/image.jpg" />
8
- <% end %>
9
- <% end %>