actionview-component 1.3.6 → 1.4.0

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