actionview-component 1.3.6 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2e23869593e0482d71b3ea0b082ad050fe3c9a5e1ad4b5e05e5cac866c7f31d
4
- data.tar.gz: 9ce7d1453f35246746f90153959c9848baa221042eec5a6376aae59c1433d3f7
3
+ metadata.gz: 30f9a7b6c2e7f0549743bf979ce3db726539f9c1f9391cb4f2f34e6d955b8689
4
+ data.tar.gz: 6db7efd3c77edbdacfa376d78729c8092848fc48176378a0677b50e6cd110433
5
5
  SHA512:
6
- metadata.gz: 11bacc8bde616fb29d27bf8fc7b56d5cea3d42c645fd425e56ea638427755ebb89c6b6575702b37f18cf33e56026a7fdbe11582be0bdd610deaea236043ba736
7
- data.tar.gz: 59a4a1e938c98e5c942581181e11f4af00d1ac36e49dace1ad42dfc1b10ca8da6cb589d68b07a7adcd3f6a80facab0621cd5be03e1e2eaf082dd18bf0daaf305
6
+ metadata.gz: bd86da9ed9d6be7b8397711cde1da635d657630b1864da03e297e87596395476d84bbafb4c84447b4518207ead5cbe2f905b6b7fc95f10b96cfe0b2bd5e9fcc5
7
+ data.tar.gz: d42213b009dfbd75489be053daab29fa0c74614f8657d308a08254c72ba31327933d0683d5c4ad6df43d1cdf63b86236ef3cda9e6b23b9b883ba987c1692b719
@@ -8,22 +8,18 @@ jobs:
8
8
  strategy:
9
9
  matrix:
10
10
  rails_version: [5.0.0, 5.2.3, 6.0.0, master]
11
- ruby_version: [2.3.x, 2.4.x, 2.5.x, 2.6.x]
11
+ ruby_version: [2.4.x, 2.5.x, 2.6.x]
12
12
  exclude:
13
13
  - rails_version: master
14
14
  ruby_version: 2.4.x
15
- - rails_version: master
16
- ruby_version: 2.3.x
17
15
  - rails_version: 6.0.0
18
16
  ruby_version: 2.4.x
19
- - rails_version: 6.0.0
20
- ruby_version: 2.3.x
21
17
  steps:
22
18
  - uses: actions/checkout@master
23
19
  - name: Setup Ruby
24
20
  uses: actions/setup-ruby@v1
25
21
  with:
26
- version: ${{ matrix.ruby_version }}
22
+ ruby-version: ${{ matrix.ruby_version }}
27
23
  - name: Build and test with Rake
28
24
  run: |
29
25
  gem install bundler:1.14.0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ # v1.4.0
2
+
3
+ * Fix bug where components broke in application paths with periods.
4
+
5
+ *Anton, Joel Hawksley*
6
+
7
+ * Add support for `cache_if` in component templates.
8
+
9
+ *Aaron Patterson, Joel Hawksley*
10
+
11
+ * Add support for variants.
12
+
13
+ *Juan Manuel Ramallo*
14
+
15
+ * Fix bug in virtual path lookup.
16
+
17
+ *Juan Manuel Ramallo*
18
+
19
+ * Preselect the rendered component in render_inline.
20
+
21
+ *Elia Schito*
22
+
1
23
  # v1.3.6
2
24
 
3
25
  * Allow template file names without format.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- actionview-component (1.3.6)
4
+ actionview-component (1.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -168,7 +168,7 @@ DEPENDENCIES
168
168
  minitest (= 5.1.0)
169
169
  rails (= 6.0.0)
170
170
  rake (~> 10.0)
171
- rubocop (~> 0.59)
171
+ rubocop (= 0.74)
172
172
  rubocop-github (~> 0.13.0)
173
173
  slim (~> 4.0)
174
174
 
data/README.md CHANGED
@@ -15,7 +15,7 @@ As the goal of this gem is to be upstreamed into Rails, it is designed to integr
15
15
 
16
16
  ## Compatibility
17
17
 
18
- `actionview-component` is tested for compatibility with combinations of Ruby `2.3`/`2.4`/`2.5`/`2.6` and Rails `5.0.0`/`5.2.3`/`6.0.0`/`6.1.0.alpha`.
18
+ `actionview-component` is tested for compatibility with combinations of Ruby `2.4`/`2.5`/`2.6` and Rails `5.0.0`/`5.2.3`/`6.0.0`/`6.1.0.alpha`.
19
19
 
20
20
  ## Installation
21
21
  Add this line to your application's Gemfile:
@@ -178,7 +178,7 @@ class MyComponentTest < Minitest::Test
178
178
  def test_render_component
179
179
  assert_equal(
180
180
  %(<span title="my title">Hello, World!</span>),
181
- render_inline(TestComponent, title: "my title") { "Hello, World!" }.css("span").to_html
181
+ render_inline(TestComponent, title: "my title") { "Hello, World!" }.to_html
182
182
  )
183
183
  end
184
184
  end
@@ -186,6 +186,21 @@ end
186
186
 
187
187
  In general, we’ve found it makes the most sense to test components based on their rendered HTML.
188
188
 
189
+ #### Action Pack Variants
190
+
191
+ To test a specific variant you can wrap your test with the `with_variant` helper method as:
192
+
193
+ ```ruby
194
+ def test_render_component_for_tablet
195
+ with_variant :tablet do
196
+ assert_equal(
197
+ %(<span title="my title">Hello, tablets!</span>),
198
+ render_inline(TestComponent, title: "my title") { "Hello, tablets!" }.css("span").to_html
199
+ )
200
+ end
201
+ end
202
+ ```
203
+
189
204
  ## Frequently Asked Questions
190
205
 
191
206
  ### Can I use other templating languages besides ERB?
@@ -39,6 +39,6 @@ Gem::Specification.new do |spec|
39
39
  spec.add_development_dependency "minitest", "= 5.1.0"
40
40
  spec.add_development_dependency "haml", "~> 5"
41
41
  spec.add_development_dependency "slim", "~> 4.0"
42
- spec.add_development_dependency "rubocop", "~> 0.59"
42
+ spec.add_development_dependency "rubocop", "= 0.74"
43
43
  spec.add_development_dependency "rubocop-github", "~> 0.13.0"
44
44
  end
@@ -9,7 +9,7 @@ class ActionView::Base
9
9
  def render(options = {}, args = {}, &block)
10
10
  if options.respond_to?(:render_in)
11
11
  ActiveSupport::Deprecation.warn(
12
- "passing component instances to `render` has been deprecated and will be removed in v2.0.0. Use `render MyComponent, foo: :bar` instead."
12
+ "passing component instances (`render MyComponent.new(foo: :bar)`) has been deprecated and will be removed in v2.0.0. Use `render MyComponent, foo: :bar` instead."
13
13
  )
14
14
 
15
15
  options.render_in(self, &block)
@@ -33,6 +33,8 @@ module ActionView
33
33
  include ActiveSupport::Configurable
34
34
  include ActionController::RequestForgeryProtection
35
35
 
36
+ validate :variant_exists
37
+
36
38
  # Entrypoint for rendering components. Called by ActionView::Base#render.
37
39
  #
38
40
  # view_context: ActionView context from calling view
@@ -65,10 +67,16 @@ module ActionView
65
67
  @lookup_context ||= view_context.lookup_context
66
68
  @view_flow ||= view_context.view_flow
67
69
  @virtual_path ||= virtual_path
70
+ @variant = @lookup_context.variants.first
71
+ old_current_template = @current_template
72
+ @current_template = self
68
73
 
69
74
  @content = view_context.capture(&block) if block_given?
70
75
  validate!
71
- call
76
+
77
+ send(self.class.call_method_name(@variant))
78
+ ensure
79
+ @current_template = old_current_template
72
80
  end
73
81
 
74
82
  def initialize(*); end
@@ -88,9 +96,31 @@ module ActionView
88
96
  # Looks for the source file path of the initialize method of the instance's class.
89
97
  # Removes the first part of the path and the extension.
90
98
  def virtual_path
91
- self.class.source_location.gsub(%r{(.*app/)|(.rb)}, "")
99
+ self.class.source_location.gsub(%r{(.*app/)|(\.rb)}, "")
100
+ end
101
+
102
+ def view_cache_dependencies
103
+ []
104
+ end
105
+
106
+ def format # :nodoc:
107
+ @variant
108
+ end
109
+
110
+ private
111
+
112
+ def variant_exists
113
+ return if self.class.variants.include?(@variant) || @variant.nil?
114
+
115
+ errors.add(:variant, "'#{@variant}' has no template defined")
92
116
  end
93
117
 
118
+ def request
119
+ @request ||= controller.request
120
+ end
121
+
122
+ attr_reader :content, :view_context
123
+
94
124
  class << self
95
125
  def inherited(child)
96
126
  child.include Rails.application.routes.url_helpers unless child < Rails.application.routes.url_helpers
@@ -98,64 +128,90 @@ module ActionView
98
128
  super
99
129
  end
100
130
 
131
+ def call_method_name(variant)
132
+ if variant.present?
133
+ "call_#{variant}"
134
+ else
135
+ "call"
136
+ end
137
+ end
138
+
101
139
  def source_location
140
+ # Require #initialize to be defined so that we can use
141
+ # method#source_location to look up the file name
142
+ # of the component.
143
+ #
144
+ # If we were able to only support Ruby 2.7+,
145
+ # We could just use Module#const_source_location,
146
+ # rendering this unnecessary.
147
+ raise NotImplementedError.new("#{self} must implement #initialize.") unless self.instance_method(:initialize).owner == self
148
+
102
149
  instance_method(:initialize).source_location[0]
103
150
  end
104
151
 
105
- # Compile template to #call instance method, assuming it hasn't been compiled already.
152
+ # Compile templates to instance methods, assuming they haven't been compiled already.
106
153
  # We could in theory do this on app boot, at least in production environments.
107
- # Right now this just compiles the template the first time the component is rendered.
154
+ # Right now this just compiles the first time the component is rendered.
108
155
  def compile
109
156
  return if @compiled && ActionView::Base.cache_template_loading
110
- ensure_initializer_defined
111
157
 
112
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
113
- def call
114
- @output_buffer = ActionView::OutputBuffer.new
115
- #{compiled_template}
116
- end
117
- RUBY
158
+ validate_templates
159
+
160
+ templates.each do |template|
161
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
162
+ def #{call_method_name(template[:variant])}
163
+ @output_buffer = ActionView::OutputBuffer.new
164
+ #{compiled_template(template[:path])}
165
+ end
166
+ RUBY
167
+ end
118
168
 
119
169
  @compiled = true
120
170
  end
121
171
 
172
+ def variants
173
+ templates.map { |template| template[:variant] }
174
+ end
175
+
122
176
  private
123
177
 
124
- # Require #initialize to be defined so that we can use
125
- # method#source_location to look up the file name
126
- # of the component.
127
- #
128
- # If we were able to only support Ruby 2.7+,
129
- # We could just use Module#const_source_location,
130
- # rendering this unnecessary.
131
- def ensure_initializer_defined
132
- raise NotImplementedError.new("#{self} must implement #initialize.") unless self.instance_method(:initialize).owner == self
178
+ def templates
179
+ @templates ||=
180
+ (Dir["#{source_location.sub(/#{File.extname(source_location)}$/, '')}.*{#{ActionView::Template.template_handler_extensions.join(',')}}"] - [source_location]).each_with_object([]) do |path, memo|
181
+ memo << {
182
+ path: path,
183
+ variant: path.split(".").second.split("+")[1]&.to_sym,
184
+ handler: path.split(".").last
185
+ }
186
+ end
133
187
  end
134
188
 
135
- def compiled_template
136
- handler = ActionView::Template.handler_for_extension(File.extname(template_file_path).gsub(".", ""))
137
- template = File.read(template_file_path)
189
+ def validate_templates
190
+ if templates.empty?
191
+ raise NotImplementedError.new("Could not find a template file for #{self}.")
192
+ end
138
193
 
139
- if handler.method(:call).parameters.length > 1
140
- handler.call(DummyTemplate.new, template)
141
- else
142
- handler.call(DummyTemplate.new(template))
194
+ if templates.select { |template| template[:variant].nil? }.length > 1
195
+ raise StandardError.new("More than one template found for #{self}. There can only be one default template file per component.")
143
196
  end
144
- end
145
197
 
146
- def template_file_path
147
- sibling_template_files =
148
- Dir["#{source_location.split(".")[0]}.*{#{ActionView::Template.template_handler_extensions.join(',')}}"] - [source_location]
198
+ variants.each_with_object(Hash.new(0)) { |variant, counts| counts[variant] += 1 }.each do |variant, count|
199
+ next unless count > 1
149
200
 
150
- if sibling_template_files.length > 1
151
- raise StandardError.new("More than one template found for #{self}. There can only be one sidecar template file per component.")
201
+ raise StandardError.new("More than one template found for variant '#{variant}' in #{self}. There can only be one template file per variant.")
152
202
  end
203
+ end
153
204
 
154
- if sibling_template_files.length == 0
155
- raise NotImplementedError.new("Could not find a template file for #{self}.")
156
- end
205
+ def compiled_template(file_path)
206
+ handler = ActionView::Template.handler_for_extension(File.extname(file_path).gsub(".", ""))
207
+ template = File.read(file_path)
157
208
 
158
- sibling_template_files[0]
209
+ # This can be removed once this code is merged into Rails
210
+ if handler.method(:call).parameters.length > 1
211
+ handler.call(DummyTemplate.new, template)
212
+ else
213
+ handler.call(DummyTemplate.new(template))
214
+ end
159
215
  end
160
216
  end
161
217
 
@@ -175,14 +231,6 @@ module ActionView
175
231
  "text/html"
176
232
  end
177
233
  end
178
-
179
- private
180
-
181
- def request
182
- @request ||= controller.request
183
- end
184
-
185
- attr_reader :content, :view_context
186
234
  end
187
235
  end
188
236
  end
@@ -4,7 +4,7 @@ module ActionView
4
4
  module Component
5
5
  module TestHelpers
6
6
  def render_inline(component, **args, &block)
7
- Nokogiri::HTML(controller.view_context.render(component, args, &block))
7
+ Nokogiri::HTML(controller.view_context.render(component, args, &block)).css("body > *")
8
8
  end
9
9
 
10
10
  def controller
@@ -22,6 +22,14 @@ module ActionView
22
22
 
23
23
  render_inline(component, args, &block)
24
24
  end
25
+
26
+ def with_variant(variant)
27
+ old_variants = controller.view_context.lookup_context.variants
28
+
29
+ controller.view_context.lookup_context.variants = variant
30
+ yield
31
+ controller.view_context.lookup_context.variants = old_variants
32
+ end
25
33
  end
26
34
  end
27
35
  end
@@ -4,8 +4,8 @@ module ActionView
4
4
  module Component
5
5
  module VERSION
6
6
  MAJOR = 1
7
- MINOR = 3
8
- PATCH = 6
7
+ MINOR = 4
8
+ PATCH = 0
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH].join(".")
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionview-component
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.6
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-21 00:00:00.000000000 Z
11
+ date: 2019-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - '='
88
88
  - !ruby/object:Gem::Version
89
- version: '0.59'
89
+ version: '0.74'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - '='
95
95
  - !ruby/object:Gem::Version
96
- version: '0.59'
96
+ version: '0.74'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rubocop-github
99
99
  requirement: !ruby/object:Gem::Requirement