view_component-fragment_caching 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cc762ffb0feb715f3c3bf3d621c783871453926765109c40d4cd827f50494580
4
+ data.tar.gz: 717f0a81506a140d71d0b717149662fe073eb006bdfd0cbeae59f2419bf9a6a3
5
+ SHA512:
6
+ metadata.gz: 642b6eef5c8669122224a26b1090f3bb1ce242c1b322126c967effc4361048014de35486007aeb8d7f7a9f3dff7b60ce8c4e7fa100cc9391de787112944c30ae
7
+ data.tar.gz: a523f790eaf7747ab425a5d5279294fa5f96bfcf3d958fe50f1472ecc3c5083cf13e882e0ccebee5ea7ff4b2620a4b7062751ed88680b060a8c7609688080617
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Patrick Arnett
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # ViewComponent::FragmentCaching
2
+ With fragment caching in Rails, updates to a partial's source code will automatically bust
3
+ appropriate caches in which the partial is detectable as a dependency.
4
+
5
+ This gem augments ActionView's fragment caching strategy to detect and parse view components. In addition to digesting a
6
+ component's view file (if present), it will also digest the component's ruby file and those of any superclasses
7
+ descended from `ViewComponent::Base`.
8
+
9
+ ## Installation
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem "view_component-fragment_caching"
14
+ ```
15
+
16
+ And then execute:
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+ ```bash
23
+ $ gem install view_component-fragment_caching
24
+ ```
25
+
26
+ ## Configuration
27
+ By default, view components will be detected in `ViewComponent::Base.view_component_path` (`app/components` typically).
28
+ This can be configured in an initializer:
29
+
30
+ ```ruby
31
+ ViewComponent::FragmentCaching.configure do |c|
32
+ c.view_component_paths = %w(
33
+ app/components
34
+ app/additional_components
35
+ )
36
+ end
37
+ ```
38
+
39
+ ## Use
40
+ It is important that naming conventions are followed. The tracker will ignore components whose class names do not end with "Component".
41
+
42
+ #### Render dependencies
43
+ ```erb
44
+ <%= render Users::AuthorComponent.new(...) %>
45
+ <%= render Users::AuthorComponent.with_collection(...) %>
46
+ ```
47
+
48
+ #### Explicit dependencies
49
+ ```erb
50
+ <%# View Component Dependency: Users::AuthorComponent %>
51
+ <%= render @author %>
52
+ ```
53
+
54
+ ## Contributing
55
+ Please follow conventions and write tests.
56
+ ```bash
57
+ $ bundle exec rspec
58
+ $ bundle exec rubocop
59
+ ```
60
+
61
+ ## License
62
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+
3
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
4
+ load 'rails/tasks/engine.rake'
5
+
6
+ load 'rails/tasks/statistics.rake'
7
+
8
+ require 'bundler/gem_tasks'
@@ -0,0 +1,19 @@
1
+ ActiveSupport.on_load :action_controller do
2
+ next unless self == ActionController::Base
3
+
4
+ ViewComponent::FragmentCaching.initialize! context: self
5
+ end
6
+
7
+ Rails.application.reloader.to_prepare do
8
+ ActiveSupport.on_load :action_view do
9
+ ActiveSupport.on_load :after_initialize do
10
+ ActionView::DependencyTracker::ERBTracker.prepend ViewComponent::FragmentCaching::Trackers::ViewComponentTracking
11
+ ActionView::Digestor::Node.prepend ViewComponent::FragmentCaching::Digestors::WithViewComponentRb
12
+
13
+ ViewComponent::FragmentCaching::Resolvers::ViewComponentResolver::VIEW_COMPONENT_RUBY_HANDLER.tap do |vc_rb|
14
+ ActionView::Template.register_template_handler vc_rb, ActionView::Template::Handlers::ERB.new
15
+ ActionView::DependencyTracker.register_tracker vc_rb, ActionView::DependencyTracker::ERBTracker
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ require 'view_component'
2
+
3
+ module ViewComponent
4
+ module FragmentCaching
5
+ class Configuration
6
+ attr_reader :view_component_paths
7
+
8
+ def initialize
9
+ self.view_component_path = ::ViewComponent::Base.view_component_path
10
+ end
11
+
12
+ def view_component_paths=(path_or_paths)
13
+ @view_component_paths = Array(path_or_paths)
14
+ end
15
+ alias view_component_path= view_component_paths=
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,80 @@
1
+ module ViewComponent
2
+ module FragmentCaching
3
+ module Digestors
4
+ module WithViewComponentRb
5
+ class << self
6
+ def prepended(mod)
7
+ mod.singleton_class.prepend ClassMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def create(name, logical_name, template, partial)
13
+ klass = partial ? ActionView::Digestor::Partial : ActionView::Digestor::Node
14
+ children = ruby_nodes name, template, klass
15
+ klass.new name, logical_name, template, children
16
+ end
17
+
18
+ private
19
+
20
+ def ruby_nodes(name, template, klass)
21
+ identifier = template.identifier
22
+ if identifier.end_with?('.rb') || !identifier.match?(view_component_path_regex) ||
23
+ (component_class = name.classify.safe_constantize).nil?
24
+
25
+ return []
26
+ end
27
+
28
+ view_component_ancestors(component_class).map do |ancestor|
29
+ anc_identifier = ancestor.source_location
30
+ anc_logical_name = ancestor.name.underscore
31
+ view_component_ruby_node anc_identifier, anc_logical_name, template, klass
32
+ end
33
+ end
34
+
35
+ def view_component_path_regex
36
+ @view_component_path_regex ||=
37
+ begin
38
+ paths =
39
+ ViewComponent::FragmentCaching.view_component_paths.map do |path|
40
+ "/#{path}/".gsub Regexp.new('/{2,}'), '/'
41
+ end
42
+ Regexp.new paths.join '|'
43
+ end
44
+ end
45
+
46
+ def view_component_ancestors(component_class)
47
+ ancestors = component_class.ancestors
48
+ return [] unless ancestors.include? ViewComponent::Base
49
+
50
+ ancestors.each_with_object [] do |ancestor, memo|
51
+ return memo if ancestor == ViewComponent::Base
52
+
53
+ memo << ancestor if ancestor.is_a? Class
54
+ end
55
+ end
56
+
57
+ def view_component_ruby_node(identifier, logical_name, template, klass)
58
+ "#{identifier.split('.').first}.rb".then do |rb_identifier|
59
+ next unless File.exist? rb_identifier
60
+
61
+ rb_source = File.read rb_identifier
62
+ rb_template = new_ruby_template rb_source, rb_identifier, template, logical_name
63
+ klass.new logical_name, logical_name, rb_template, []
64
+ end
65
+ end
66
+
67
+ def new_ruby_template(rb_source, rb_identifier, template, virtual_path)
68
+ ActionView::Template.new rb_source,
69
+ rb_identifier,
70
+ template.handler,
71
+ locals: template.locals,
72
+ format: template.format,
73
+ variant: template.variant,
74
+ virtual_path: virtual_path
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,7 @@
1
+ module ViewComponent
2
+ module FragmentCaching
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace ViewComponent::FragmentCaching
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,129 @@
1
+ require 'action_view'
2
+
3
+ module ViewComponent
4
+ module FragmentCaching
5
+ module Resolvers
6
+ class ViewComponentResolver < ActionView::FileSystemResolver
7
+ VIEW_COMPONENT_RUBY_HANDLER = :vc_rb
8
+ public_constant :VIEW_COMPONENT_RUBY_HANDLER
9
+
10
+ class PathParser < ActionView::Resolver::PathParser
11
+ class MatchedAttributes
12
+ FORMATS = (ActionView::Template::Types.symbols | %i(rb)).map(&Regexp.method(:escape)).join('|').freeze
13
+ private_constant :FORMATS
14
+
15
+ HANDLERS = ActionView::Template::Handlers.extensions.map(&Regexp.method(:escape)).join('|').freeze
16
+ private_constant :HANDLERS
17
+
18
+ LOCALES = '(?!rb)[a-z]{2}(?:-[A-Z]{2})?'.freeze
19
+ private_constant :LOCALES
20
+
21
+ VARIANTS = '[^.]*'.freeze
22
+ private_constant :VARIANTS
23
+
24
+ MATCH_NAMES = %i(prefix partial action locale format variant handler).freeze
25
+ private_constant :MATCH_NAMES
26
+
27
+ REGEX = %r{
28
+ \A
29
+ (?:(?<prefix>.*)/)?
30
+ (?<partial>_)?
31
+ (?<action>.*?)
32
+ (?:\.(?<locale>#{LOCALES}))??
33
+ (?:\.(?<format>#{FORMATS}))??
34
+ (?:\+(?<variant>#{VARIANTS}))??
35
+ (?:\.(?<handler>#{HANDLERS}))?
36
+ \z
37
+ }x.freeze
38
+ private_constant :REGEX
39
+
40
+ delegate :[], to: :match
41
+
42
+ def initialize(path)
43
+ @match = REGEX.match path
44
+ end
45
+
46
+ def format
47
+ matched_sym :format
48
+ end
49
+
50
+ def handler
51
+ matched_sym(:handler) || (VIEW_COMPONENT_RUBY_HANDLER if format == :rb)
52
+ end
53
+
54
+ def locale
55
+ matched_sym :locale
56
+ end
57
+
58
+ def variant
59
+ matched_sym :variant
60
+ end
61
+
62
+ def method_missing(method_name, *)
63
+ match[method_name] if MATCH_NAMES.include? method_name
64
+ end
65
+
66
+ def respond_to_missing?(method_name)
67
+ MATCH_NAMES.include? method_name
68
+ end
69
+
70
+ private
71
+
72
+ attr_reader :match
73
+
74
+ def matched_sym(key)
75
+ match[key]&.to_sym
76
+ end
77
+ end
78
+ private_constant :MatchedAttributes
79
+
80
+ def parse(path)
81
+ match = MatchedAttributes.new path
82
+ template_path = ActionView::TemplatePath.build match.action, match.prefix || '', !match.partial.nil?
83
+ details = ActionView::TemplateDetails.new match.locale, match.handler, match.format, match.variant
84
+ ParsedPath.new template_path, details
85
+ end
86
+ end
87
+ private_constant :PathParser
88
+
89
+ def initialize(path)
90
+ super
91
+ override_path_parser
92
+ end
93
+
94
+ def clear_cache
95
+ super
96
+ override_path_parser
97
+ end
98
+
99
+ # def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
100
+ def find_all(*args)
101
+ name = args.fetch 0
102
+ prefix = args.fetch 1, nil
103
+ partial = false
104
+ details = args.fetch 3, {}
105
+ given_key = args.fetch 4, nil
106
+ locals = args.fetch 5, []
107
+
108
+ key = build_key given_key, details
109
+ super name, prefix, partial, details, key, locals
110
+ end
111
+
112
+ private
113
+
114
+ def override_path_parser
115
+ @path_parser = PathParser.new
116
+ end
117
+
118
+ def build_key(key, details)
119
+ (key || ActionView::TemplateDetails::Requested.new(**details)).then do |k|
120
+ ActionView::TemplateDetails::Requested.new formats: k.formats | %i(rb),
121
+ handlers: k.handlers,
122
+ locale: k.locale,
123
+ variants: k.variants
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,96 @@
1
+ module ViewComponent
2
+ module FragmentCaching
3
+ module Trackers
4
+ module ViewComponentTracking
5
+ EXPLICIT_VIEW_COMPONENT_DEPENDENCY = /# View Component Dependency: (\S+)/.freeze
6
+ private_constant :EXPLICIT_VIEW_COMPONENT_DEPENDENCY
7
+
8
+ CAMEL_PHRASE = /(?:([A-Z]+[a-z]*[0-9]*)+)/.freeze
9
+ private_constant :CAMEL_PHRASE
10
+
11
+ MODULE_NAME = /(?:((::)?#{CAMEL_PHRASE}+)+)/.freeze
12
+ private_constant :MODULE_NAME
13
+
14
+ VIEW_COMPONENT_NAME = /(?:#{MODULE_NAME}Component)/.freeze
15
+ private_constant :VIEW_COMPONENT_NAME
16
+
17
+ MODULE_NAME_END = /(?:\b[^:])/.freeze
18
+ private_constant :MODULE_NAME_END
19
+
20
+ INVOKE_METHOD = /(?:\s*\.\s*(((public_)?send?(\s*\(?\s*):)?))/.freeze
21
+ private_constant :INVOKE_METHOD
22
+
23
+ INITIALIZE_METHOD_NAME = /(?:\b(new|with_collection)\b)/.freeze
24
+ private_constant :INITIALIZE_METHOD_NAME
25
+
26
+ VIEW_COMPONENT_RENDER_ARGUMENTS = /
27
+ \A
28
+ (?:\s*\(?\s*)
29
+ (?<view_component>#{VIEW_COMPONENT_NAME})
30
+ (?:#{INVOKE_METHOD})
31
+ (?:#{INITIALIZE_METHOD_NAME})
32
+ /xm.freeze
33
+ private_constant :VIEW_COMPONENT_RENDER_ARGUMENTS
34
+
35
+ CLASS_INHERITANCE = /(?:\s*\bclass\s+#{MODULE_NAME}\s*<\s*)/.freeze
36
+ private_constant :CLASS_INHERITANCE
37
+
38
+ VIEW_COMPONENT_INHERITANCE_DEPENDENCY = /
39
+ (?:#{CLASS_INHERITANCE})
40
+ (?<view_component>#{VIEW_COMPONENT_NAME})
41
+ (?:#{MODULE_NAME_END})
42
+ /xm.freeze
43
+ private_constant :VIEW_COMPONENT_INHERITANCE_DEPENDENCY
44
+
45
+ def dependencies
46
+ render_dependencies |
47
+ explicit_dependencies |
48
+ view_component_inheritance_dependencies |
49
+ explicit_view_component_dependencies
50
+ end
51
+
52
+ private
53
+
54
+ def render_dependencies
55
+ [].tap do |render_dependencies|
56
+ render_calls = source.split(/\brender\b/).drop(1)
57
+
58
+ render_calls.each do |arguments|
59
+ add_dependencies render_dependencies, arguments, self.class::LAYOUT_DEPENDENCY
60
+ add_dependencies render_dependencies, arguments, self.class::RENDER_ARGUMENTS
61
+ add_dependencies render_dependencies, arguments, VIEW_COMPONENT_RENDER_ARGUMENTS
62
+ end
63
+ end
64
+ end
65
+
66
+ def add_dependencies(render_dependencies, arguments, pattern)
67
+ arguments.scan pattern do
68
+ match = Regexp.last_match.named_captures.symbolize_keys
69
+ add_dynamic_dependency render_dependencies, match[:dynamic]
70
+ add_static_dependency render_dependencies, match[:static], match[:quote]
71
+ add_view_component_dependency render_dependencies, match[:view_component]
72
+ end
73
+ end
74
+
75
+ def add_view_component_dependency(dependencies, dependency)
76
+ return if dependency.blank?
77
+
78
+ path = dependency.underscore
79
+ dependencies << path unless dependencies.include? path
80
+ end
81
+
82
+ def view_component_inheritance_dependencies
83
+ scan_source VIEW_COMPONENT_INHERITANCE_DEPENDENCY
84
+ end
85
+
86
+ def explicit_view_component_dependencies
87
+ scan_source EXPLICIT_VIEW_COMPONENT_DEPENDENCY
88
+ end
89
+
90
+ def scan_source(pattern)
91
+ source.scan(pattern).flatten.uniq.map(&:underscore)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,6 @@
1
+ module ViewComponent
2
+ module FragmentCaching
3
+ VERSION = '0.2.1'.freeze
4
+ public_constant :VERSION
5
+ end
6
+ end
@@ -0,0 +1,32 @@
1
+ require 'action_view/dependency_tracker/erb_tracker'
2
+ Dir[File.expand_path './fragment_caching/**/*.rb', __dir__].sort.each(&method(:require))
3
+
4
+ module ViewComponent
5
+ module FragmentCaching
6
+ class << self
7
+ delegate :view_component_paths, to: :configuration
8
+
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ def configure
14
+ yield configuration
15
+ end
16
+
17
+ def initialize!(context:)
18
+ prepend_view_component_paths context
19
+ end
20
+
21
+ private
22
+
23
+ def prepend_view_component_paths(context)
24
+ full_paths = view_component_paths.map(&Rails.root.method(:join))
25
+ Dir[*full_paths].each do |dir|
26
+ resolver = Resolvers::ViewComponentResolver.new dir
27
+ context.prepend_view_path resolver
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: view_component-fragment_caching
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Patrick Arnett
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-06-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: view_component
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.43'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.43'
41
+ - !ruby/object:Gem::Dependency
42
+ name: capybara
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.36'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.36'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.26'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.26'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.14'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.14'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.10'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.10'
125
+ description: Monkey patch ActionView to track and digest view components within cached
126
+ fragments.
127
+ email:
128
+ - patrick.a.arnett@gmail.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - MIT-LICENSE
134
+ - README.md
135
+ - Rakefile
136
+ - config/initializers/view_component/fragment_caching.rb
137
+ - lib/view_component/fragment_caching.rb
138
+ - lib/view_component/fragment_caching/configuration.rb
139
+ - lib/view_component/fragment_caching/digestors/with_view_component_rb.rb
140
+ - lib/view_component/fragment_caching/engine.rb
141
+ - lib/view_component/fragment_caching/resolvers/view_component_resolver.rb
142
+ - lib/view_component/fragment_caching/trackers/view_component_tracking.rb
143
+ - lib/view_component/fragment_caching/version.rb
144
+ homepage:
145
+ licenses:
146
+ - MIT
147
+ metadata:
148
+ source_code_uri: https://www.github.com/patrickarnett/view_component-fragment_caching
149
+ changelog_uri: https://www.github.com/patrickarnett/view_component-fragment_caching/blob/main/CHANGELOG.md
150
+ rubygems_mfa_required: 'true'
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 2.6.0
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubygems_version: 3.2.22
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: Bust fragment caches when view components update
170
+ test_files: []