├── .gitignore ├── .ruby-version ├── .tool-versions ├── cells ├── nested_name │ ├── show.erb │ └── cell.rb └── name │ ├── show.erb │ └── cell.rb ├── partials ├── _nested_name.html.erb └── _name.html.erb ├── hanami-view ├── nested_name │ ├── view.html.erb │ └── view.rb └── name │ ├── view.html.erb │ └── view.rb ├── components ├── nested_name_component.html.erb ├── name_component.html.erb ├── name_component.rb └── nested_name_component.rb ├── assets └── benchmarks.png ├── papercraft ├── pc_nested_name_component.rb ├── pc_name_component.rb └── pc_test_page.rb ├── Rakefile ├── .rubocop.yml ├── phlex ├── phlex_nested_name_component.rb └── phlex_name_component.rb ├── benchmark.rb ├── ruby2html └── ruby2html_renderer.rb ├── Gemfile ├── test └── render_test.rb ├── .github └── workflows │ └── benchmarks.yml ├── name.html ├── renderers.rb ├── README.md └── Gemfile.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.3.4 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 3.4.1 2 | -------------------------------------------------------------------------------- /cells/nested_name/show.erb: -------------------------------------------------------------------------------- 1 |

nested hello <%= name %>

2 | -------------------------------------------------------------------------------- /partials/_nested_name.html.erb: -------------------------------------------------------------------------------- 1 |

nested hello <%= name %>

2 | -------------------------------------------------------------------------------- /hanami-view/nested_name/view.html.erb: -------------------------------------------------------------------------------- 1 |

nested hello <%= name %>

2 | -------------------------------------------------------------------------------- /components/nested_name_component.html.erb: -------------------------------------------------------------------------------- 1 |

nested hello <%= @name %>

2 | -------------------------------------------------------------------------------- /assets/benchmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KonnorRogers/view-layer-benchmarks/HEAD/assets/benchmarks.png -------------------------------------------------------------------------------- /cells/name/show.erb: -------------------------------------------------------------------------------- 1 |

hello <%= name %>

2 | 3 | <% 50.times do %> 4 | <%= NestedName::Cell.new(model).() %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /partials/_name.html.erb: -------------------------------------------------------------------------------- 1 |

hello <%= name %>

2 | 3 | <%= render partial: '/nested_name', collection: [name] * 50, as: :name %> 4 | -------------------------------------------------------------------------------- /papercraft/pc_nested_name_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | PCNestedNameComponent = ->(name) { 4 | p "nested hello #{name}" 5 | } 6 | -------------------------------------------------------------------------------- /components/name_component.html.erb: -------------------------------------------------------------------------------- 1 |

hello <%= @name %>

2 | 3 | <% 50.times do %> 4 | <%= render NestedNameComponent.new(name: @name) %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /components/name_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class NameComponent < ViewComponent::Base 4 | def initialize(name:) 5 | @name = name 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /hanami-view/name/view.html.erb: -------------------------------------------------------------------------------- 1 |

hello <%= name %>

2 | 3 | <% view = NestedName::View.new %> 4 | 5 | <% 50.times do %> 6 | <%= view.call(name: name).to_s %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /components/nested_name_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class NestedNameComponent < ViewComponent::Base 4 | def initialize(name:) 5 | @name = name 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | task :test do 4 | ruby './test/render_test.rb' 5 | end 6 | 7 | task default: :test 8 | 9 | task :benchmark do 10 | ruby './benchmark.rb' 11 | end 12 | -------------------------------------------------------------------------------- /cells/nested_name/cell.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NestedName 4 | class Cell < Cell::ViewModel 5 | include ::Cell::Erb 6 | 7 | property :name 8 | 9 | def show 10 | render 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /papercraft/pc_name_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'pc_nested_name_component' 4 | 5 | PCNameComponent = ->(name) { 6 | h1 "hello #{name}" 7 | 8 | 50.times { 9 | PCNestedNameComponent(name) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /hanami-view/name/view.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'hanami/view' 4 | 5 | module Name 6 | class View < Hanami::View 7 | config.paths = [File.join(__dir__)] 8 | config.template = 'view' 9 | 10 | expose :name 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-rails_config: 3 | - config/rails.yml 4 | 5 | Style/ClassAndModuleChildren: 6 | EnforcedStyle: nested 7 | 8 | Lint/Debugger: 9 | Enabled: true 10 | 11 | Style/StringLiterals: 12 | Enabled: true 13 | EnforcedStyle: single_quotes -------------------------------------------------------------------------------- /hanami-view/nested_name/view.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'hanami/view' 4 | 5 | module NestedName 6 | class View < Hanami::View 7 | config.paths = [File.join(__dir__)] 8 | config.template = 'view' 9 | 10 | expose :name 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /cells/name/cell.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Name 4 | class Cell < Cell::ViewModel 5 | include ::Cell::Erb # or Cell::Hamlit, or Cell::Haml, or Cell::Slim 6 | 7 | property :name 8 | 9 | def show 10 | render 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /phlex/phlex_nested_name_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class PhlexNestedNameComponent < Phlex::HTML 4 | def initialize(name:) 5 | @name = name 6 | end 7 | 8 | def view_template 9 | plain "\n " 10 | p { "nested hello #{@name}" } 11 | plain "\n" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /papercraft/pc_test_page.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'papercraft' 4 | require_relative 'pc_name_component' 5 | 6 | class PCTestPage 7 | def call(name:) 8 | page = Papercraft.html { |some_name| 9 | PCNameComponent(some_name) 10 | } 11 | page.render(name) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /benchmark.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require './renderers.rb' 4 | 5 | Benchmark.ips do |x| 6 | x.time = 10 7 | x.warmup = 10 8 | 9 | 10 | Renderers.recursive_renderers.each do |renderer, render_function| 11 | x.report(renderer.to_s) { render_function.call() } 12 | end 13 | 14 | x.compare! 15 | end 16 | -------------------------------------------------------------------------------- /phlex/phlex_name_component.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class PhlexNameComponent < Phlex::HTML 4 | def initialize(name:) 5 | @name = name 6 | end 7 | 8 | def view_template 9 | h1 { "hello #{@name}" } 10 | plain "\n" 11 | 12 | 50.times do 13 | render PhlexNestedNameComponent.new(name: @name) 14 | end 15 | 16 | plain "\n" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /ruby2html/ruby2html_renderer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Ruby2htmlRenderer 4 | def self.render(name:) 5 | Ruby2html::Render.new(nil) do 6 | h1 "hello #{name}" 7 | plain "\n" 8 | 9 | # TODO: This should probably be a component to simulate loading another file. 10 | 50.times do 11 | plain "\n " 12 | p "nested hello #{name}" 13 | plain "\n" 14 | end 15 | plain "\n" 16 | end.render 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 6 | 7 | ruby '~> 3.4' 8 | 9 | # Activate the gem you are reporting the issue against. 10 | gem 'rails', '~> 8.0' 11 | gem 'benchmark-ips', '~> 2.13' 12 | gem 'view_component' 13 | 14 | gem 'cells-rails' 15 | gem 'cells-erb' 16 | gem 'hanami-view', require: false 17 | gem 'phlex-rails' 18 | gem 'papercraft' 19 | gem 'ruby2html' 20 | 21 | gem 'rubocop-rails_config', '~> 1.16' 22 | gem 'pry' 23 | -------------------------------------------------------------------------------- /test/render_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'minitest' 4 | require 'minitest/autorun' 5 | 6 | require_relative '../renderers.rb' 7 | 8 | class RenderTest < Minitest::Test 9 | def test_that_all_benchmarks_render_the_same 10 | html = File.read(File.expand_path('../name.html', __dir__)).gsub(/\n/, '') 11 | 12 | ::Renderers.recursive_renderers.each do |library, render_function| 13 | rendered_html = render_function.call().gsub(/\n/, '') 14 | 15 | next if library == :papercraft 16 | 17 | assert_equal(html, rendered_html, "#{library} failed to match the expected HTML.") 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /.github/workflows/benchmarks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Benchmarks 3 | 4 | on: 5 | push: 6 | branches: 7 | - "main" 8 | pull_request: 9 | branches: 10 | - "*" 11 | 12 | jobs: 13 | benchmarks: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v2 19 | 20 | - name: Set up Ruby 21 | uses: ruby/setup-ruby@v1 22 | with: 23 | ruby-version: '3.4' 24 | bundler-cache: true 25 | 26 | - name: Run tests 27 | run: bundle exec rake test 28 | 29 | - name: Run benchmarks 30 | run: bundle exec rake benchmark 31 | 32 | - name: Rubocop 33 | run: bundle exec rubocop --parallel 34 | -------------------------------------------------------------------------------- /name.html: -------------------------------------------------------------------------------- 1 |

hello Fox Mulder

2 | 3 |

nested hello Fox Mulder

4 | 5 |

nested hello Fox Mulder

6 | 7 |

nested hello Fox Mulder

8 | 9 |

nested hello Fox Mulder

10 | 11 |

nested hello Fox Mulder

12 | 13 |

nested hello Fox Mulder

14 | 15 |

nested hello Fox Mulder

16 | 17 |

nested hello Fox Mulder

18 | 19 |

nested hello Fox Mulder

20 | 21 |

nested hello Fox Mulder

22 | 23 |

nested hello Fox Mulder

24 | 25 |

nested hello Fox Mulder

26 | 27 |

nested hello Fox Mulder

28 | 29 |

nested hello Fox Mulder

30 | 31 |

nested hello Fox Mulder

32 | 33 |

nested hello Fox Mulder

34 | 35 |

nested hello Fox Mulder

36 | 37 |

nested hello Fox Mulder

38 | 39 |

nested hello Fox Mulder

40 | 41 |

nested hello Fox Mulder

42 | 43 |

nested hello Fox Mulder

44 | 45 |

nested hello Fox Mulder

46 | 47 |

nested hello Fox Mulder

48 | 49 |

nested hello Fox Mulder

50 | 51 |

nested hello Fox Mulder

52 | 53 |

nested hello Fox Mulder

54 | 55 |

nested hello Fox Mulder

56 | 57 |

nested hello Fox Mulder

58 | 59 |

nested hello Fox Mulder

60 | 61 |

nested hello Fox Mulder

62 | 63 |

nested hello Fox Mulder

64 | 65 |

nested hello Fox Mulder

66 | 67 |

nested hello Fox Mulder

68 | 69 |

nested hello Fox Mulder

70 | 71 |

nested hello Fox Mulder

72 | 73 |

nested hello Fox Mulder

74 | 75 |

nested hello Fox Mulder

76 | 77 |

nested hello Fox Mulder

78 | 79 |

nested hello Fox Mulder

80 | 81 |

nested hello Fox Mulder

82 | 83 |

nested hello Fox Mulder

84 | 85 |

nested hello Fox Mulder

86 | 87 |

nested hello Fox Mulder

88 | 89 |

nested hello Fox Mulder

90 | 91 |

nested hello Fox Mulder

92 | 93 |

nested hello Fox Mulder

94 | 95 |

nested hello Fox Mulder

96 | 97 |

nested hello Fox Mulder

98 | 99 |

nested hello Fox Mulder

100 | 101 |

nested hello Fox Mulder

102 | 103 | -------------------------------------------------------------------------------- /renderers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rubygems' 4 | require 'bundler/setup' 5 | Bundler.require(:default) 6 | require 'rack/test' 7 | require 'action_controller/railtie' 8 | require 'benchmark/ips' 9 | 10 | # Configure Rails Environment 11 | ENV['RAILS_ENV'] = 'production' 12 | 13 | 14 | require_relative './components/name_component' 15 | require_relative './components/nested_name_component' 16 | 17 | require_relative './cells/name/cell' 18 | require_relative './cells/nested_name/cell' 19 | 20 | require_relative './hanami-view/name/view' 21 | require_relative './hanami-view/nested_name/view' 22 | 23 | require_relative './phlex/phlex_name_component' 24 | require_relative './phlex/phlex_nested_name_component' 25 | 26 | require_relative './papercraft/pc_test_page' 27 | 28 | require_relative './ruby2html/ruby2html_renderer' 29 | 30 | module Cell 31 | class ViewModel 32 | self.view_paths = ['cells'] 33 | end 34 | end 35 | 36 | 37 | 38 | module Renderers 39 | class BenchmarksController < ActionController::Base; end 40 | 41 | BenchmarksController.view_paths = %w[./partials ./ruby2html] 42 | 43 | class TestApp < Rails::Application 44 | config.root = __dir__ 45 | config.hosts << 'example.org' 46 | config.session_store :cookie_store, key: 'cookie_store_key' 47 | credentials.secret_key_base = 'secret_key_base' 48 | 49 | config.logger = Logger.new($stdout) 50 | Rails.logger = config.logger 51 | end 52 | 53 | class NameObj 54 | attr_accessor :name 55 | 56 | def initialize(name) 57 | @name = name 58 | end 59 | end 60 | 61 | def self.view_context 62 | @_view_context ||= BenchmarksController.new.view_context 63 | end 64 | 65 | # A recursive render calls a "name" template 50 times and is fairly simple. 66 | def self.recursive_renderers 67 | { 68 | view_component: proc { view_context.render(NameComponent.new(name: 'Fox Mulder')) }, 69 | partials: proc { view_context.render('/name', name: 'Fox Mulder') }, 70 | trailblazer_cells: proc { view_context.render(html: Name::Cell.new(NameObj.new('Fox Mulder')).().html_safe) }, 71 | hanami_view: proc { view_context.render(html: Name::View.new.call(name: 'Fox Mulder').to_s.html_safe) }, 72 | phlex: proc { view_context.render(PhlexNameComponent.new(name: 'Fox Mulder')) }, 73 | papercraft: proc { view_context.render(html: PCTestPage.new.call(name: 'Fox Mulder')).to_s }, 74 | ruby2html: proc { view_context.render(html: Ruby2htmlRenderer.render(name: 'Fox Mulder')) }, 75 | } 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Purpose 2 | 3 | To document performance of ViewComponent, Rails Partials, 4 | Hanami View, Trailblazer Cells, Papercraft and Phlex Components. 5 | 6 | * ViewComponent: [viewcomponent.org](https://viewcomponent.org/) 7 | * Rails Partials: [guides.rubyonrails.org/layouts_and_rendering.html#using-partials](https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials) 8 | * Hanami View: [guides.hanamirb.org/views/overview](https://guides.hanamirb.org/views/overview/) 9 | * Trailblazer Cells: [trailblazer.to/2.1/docs/cells](https://trailblazer.to/2.1/docs/cells/) 10 | * Papercraft: [github.com/digital-fabric/papercraft](https://github.com/digital-fabric/papercraft) 11 | * Phlex Components: [phlex.fun](https://www.phlex.fun/) 12 | 13 | ## Benchmarks 14 | 15 | Benchmarks arent representative of real life and just 16 | render nested components / partials. Take all numbers with 17 | a grain of salt. 18 | 19 | ## Contributing 20 | 21 | Feel free to submit a PR for optimization of views and 22 | other use-cases / view layers. 23 | 24 | ## Setup 25 | 26 | ```bash 27 | git clone https://github.com/paramagicdev/view-layer-benchmarks.git 28 | cd view-layer-benchmarks 29 | bundle install 30 | RAILS_ENV=production bundle exec rake benchmark 31 | ``` 32 | 33 | Benchmarks are for Ruby 3.4.1 and Rails v8.0. 34 | 35 | **RE-RUN THESE BENCHMARKS** and run them 36 | ``` 37 | ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24] 38 | Warming up -------------------------------------- 39 | view_component 1.872k i/100ms 40 | partials 1.527k i/100ms 41 | trailblazer_cells 566.000 i/100ms 42 | hanami_view 231.000 i/100ms 43 | phlex 2.059k i/100ms 44 | papercraft 2.814k i/100ms 45 | ruby2html 2.488k i/100ms 46 | Calculating ------------------------------------- 47 | view_component 18.445k (± 0.3%) i/s (54.21 μs/i) - 185.328k in 10.047597s 48 | partials 14.946k (± 0.6%) i/s (66.91 μs/i) - 149.646k in 10.012969s 49 | trailblazer_cells 5.597k (± 1.0%) i/s (178.67 μs/i) - 56.034k in 10.012828s 50 | hanami_view 2.325k (± 0.4%) i/s (430.12 μs/i) - 23.331k in 10.035342s 51 | phlex 20.551k (± 0.3%) i/s (48.66 μs/i) - 205.900k in 10.018915s 52 | papercraft 28.002k (± 0.4%) i/s (35.71 μs/i) - 281.400k in 10.049340s 53 | ruby2html 24.851k (± 0.3%) i/s (40.24 μs/i) - 248.800k in 10.011760s 54 | 55 | Comparison: 56 | papercraft: 28002.4 i/s 57 | ruby2html: 24850.9 i/s - 1.13x slower 58 | phlex: 20551.4 i/s - 1.36x slower 59 | view_component: 18445.2 i/s - 1.52x slower 60 | partials: 14945.7 i/s - 1.87x slower 61 | trailblazer_cells: 5596.8 i/s - 5.00x slower 62 | hanami_view: 2324.9 i/s - 12.04x slower 63 | ``` 64 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (8.0.2) 5 | actionpack (= 8.0.2) 6 | activesupport (= 8.0.2) 7 | nio4r (~> 2.0) 8 | websocket-driver (>= 0.6.1) 9 | zeitwerk (~> 2.6) 10 | actionmailbox (8.0.2) 11 | actionpack (= 8.0.2) 12 | activejob (= 8.0.2) 13 | activerecord (= 8.0.2) 14 | activestorage (= 8.0.2) 15 | activesupport (= 8.0.2) 16 | mail (>= 2.8.0) 17 | actionmailer (8.0.2) 18 | actionpack (= 8.0.2) 19 | actionview (= 8.0.2) 20 | activejob (= 8.0.2) 21 | activesupport (= 8.0.2) 22 | mail (>= 2.8.0) 23 | rails-dom-testing (~> 2.2) 24 | actionpack (8.0.2) 25 | actionview (= 8.0.2) 26 | activesupport (= 8.0.2) 27 | nokogiri (>= 1.8.5) 28 | rack (>= 2.2.4) 29 | rack-session (>= 1.0.1) 30 | rack-test (>= 0.6.3) 31 | rails-dom-testing (~> 2.2) 32 | rails-html-sanitizer (~> 1.6) 33 | useragent (~> 0.16) 34 | actiontext (8.0.2) 35 | actionpack (= 8.0.2) 36 | activerecord (= 8.0.2) 37 | activestorage (= 8.0.2) 38 | activesupport (= 8.0.2) 39 | globalid (>= 0.6.0) 40 | nokogiri (>= 1.8.5) 41 | actionview (8.0.2) 42 | activesupport (= 8.0.2) 43 | builder (~> 3.1) 44 | erubi (~> 1.11) 45 | rails-dom-testing (~> 2.2) 46 | rails-html-sanitizer (~> 1.6) 47 | activejob (8.0.2) 48 | activesupport (= 8.0.2) 49 | globalid (>= 0.3.6) 50 | activemodel (8.0.2) 51 | activesupport (= 8.0.2) 52 | activerecord (8.0.2) 53 | activemodel (= 8.0.2) 54 | activesupport (= 8.0.2) 55 | timeout (>= 0.4.0) 56 | activestorage (8.0.2) 57 | actionpack (= 8.0.2) 58 | activejob (= 8.0.2) 59 | activerecord (= 8.0.2) 60 | activesupport (= 8.0.2) 61 | marcel (~> 1.0) 62 | activesupport (8.0.2) 63 | base64 64 | benchmark (>= 0.3) 65 | bigdecimal 66 | concurrent-ruby (~> 1.0, >= 1.3.1) 67 | connection_pool (>= 2.2.5) 68 | drb 69 | i18n (>= 1.6, < 2) 70 | logger (>= 1.4.2) 71 | minitest (>= 5.1) 72 | securerandom (>= 0.3) 73 | tzinfo (~> 2.0, >= 2.0.5) 74 | uri (>= 0.13.1) 75 | ast (2.4.3) 76 | base64 (0.2.0) 77 | benchmark (0.4.0) 78 | benchmark-ips (2.14.0) 79 | bigdecimal (3.1.9) 80 | builder (3.3.0) 81 | cells (4.1.8) 82 | declarative-builder (~> 0.2.0) 83 | declarative-option (< 0.2.0) 84 | tilt (>= 1.4, < 3) 85 | uber (< 0.2.0) 86 | cells-erb (0.1.0) 87 | cells (~> 4.0) 88 | erbse (>= 0.1.1) 89 | cells-rails (0.1.6) 90 | actionpack (>= 5.0) 91 | cells (>= 4.1.6, < 5.0.0) 92 | coderay (1.1.3) 93 | concurrent-ruby (1.3.4) 94 | connection_pool (2.5.0) 95 | crass (1.0.6) 96 | date (3.4.1) 97 | declarative-builder (0.2.0) 98 | trailblazer-option (~> 0.1.0) 99 | declarative-option (0.1.0) 100 | drb (2.2.1) 101 | dry-configurable (1.3.0) 102 | dry-core (~> 1.1) 103 | zeitwerk (~> 2.6) 104 | dry-core (1.1.0) 105 | concurrent-ruby (~> 1.0) 106 | logger 107 | zeitwerk (~> 2.6) 108 | dry-inflector (1.2.0) 109 | erbse (0.1.4) 110 | temple 111 | erubi (1.13.1) 112 | escape_utils (1.3.0) 113 | globalid (1.2.1) 114 | activesupport (>= 6.1) 115 | hanami-view (2.2.1) 116 | dry-configurable (~> 1.0) 117 | dry-core (~> 1.0) 118 | dry-inflector (~> 1.0, < 2) 119 | temple (~> 0.10.0, >= 0.10.2) 120 | tilt (~> 2.3) 121 | zeitwerk (~> 2.6) 122 | htmlbeautifier (1.4.3) 123 | i18n (1.14.7) 124 | concurrent-ruby (~> 1.0) 125 | io-console (0.8.0) 126 | irb (1.15.1) 127 | pp (>= 0.6.0) 128 | rdoc (>= 4.0.0) 129 | reline (>= 0.4.2) 130 | json (2.10.2) 131 | kramdown (2.5.1) 132 | rexml (>= 3.3.9) 133 | kramdown-parser-gfm (1.1.0) 134 | kramdown (~> 2.0) 135 | language_server-protocol (3.17.0.4) 136 | lint_roller (1.1.0) 137 | logger (1.7.0) 138 | loofah (2.24.0) 139 | crass (~> 1.0.2) 140 | nokogiri (>= 1.12.0) 141 | mail (2.8.1) 142 | mini_mime (>= 0.1.1) 143 | net-imap 144 | net-pop 145 | net-smtp 146 | marcel (1.0.4) 147 | method_source (1.1.0) 148 | mini_mime (1.1.5) 149 | minitest (5.25.5) 150 | net-imap (0.5.6) 151 | date 152 | net-protocol 153 | net-pop (0.1.2) 154 | net-protocol 155 | net-protocol (0.2.2) 156 | timeout 157 | net-smtp (0.5.1) 158 | net-protocol 159 | nio4r (2.7.4) 160 | nokogiri (1.18.7-aarch64-linux-gnu) 161 | racc (~> 1.4) 162 | nokogiri (1.18.7-arm64-darwin) 163 | racc (~> 1.4) 164 | nokogiri (1.18.7-x86_64-darwin) 165 | racc (~> 1.4) 166 | nokogiri (1.18.7-x86_64-linux-gnu) 167 | racc (~> 1.4) 168 | papercraft (1.4) 169 | escape_utils (~> 1.3.0) 170 | kramdown (~> 2.5.1) 171 | kramdown-parser-gfm (~> 1.1.0) 172 | rouge (~> 4.5.1) 173 | sirop (~> 0.5) 174 | parallel (1.26.3) 175 | parser (3.3.7.4) 176 | ast (~> 2.4.1) 177 | racc 178 | phlex (2.1.2) 179 | phlex-rails (2.1.3) 180 | phlex (~> 2.1.2) 181 | railties (>= 7.1, < 9) 182 | pp (0.6.2) 183 | prettyprint 184 | prettyprint (0.2.0) 185 | prism (0.27.0) 186 | pry (0.15.2) 187 | coderay (~> 1.1) 188 | method_source (~> 1.0) 189 | psych (5.2.3) 190 | date 191 | stringio 192 | racc (1.8.1) 193 | rack (3.1.12) 194 | rack-session (2.1.0) 195 | base64 (>= 0.1.0) 196 | rack (>= 3.0.0) 197 | rack-test (2.2.0) 198 | rack (>= 1.3) 199 | rackup (2.2.1) 200 | rack (>= 3) 201 | rails (8.0.2) 202 | actioncable (= 8.0.2) 203 | actionmailbox (= 8.0.2) 204 | actionmailer (= 8.0.2) 205 | actionpack (= 8.0.2) 206 | actiontext (= 8.0.2) 207 | actionview (= 8.0.2) 208 | activejob (= 8.0.2) 209 | activemodel (= 8.0.2) 210 | activerecord (= 8.0.2) 211 | activestorage (= 8.0.2) 212 | activesupport (= 8.0.2) 213 | bundler (>= 1.15.0) 214 | railties (= 8.0.2) 215 | rails-dom-testing (2.2.0) 216 | activesupport (>= 5.0.0) 217 | minitest 218 | nokogiri (>= 1.6) 219 | rails-html-sanitizer (1.6.2) 220 | loofah (~> 2.21) 221 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 222 | railties (8.0.2) 223 | actionpack (= 8.0.2) 224 | activesupport (= 8.0.2) 225 | irb (~> 1.13) 226 | rackup (>= 1.0.0) 227 | rake (>= 12.2) 228 | thor (~> 1.0, >= 1.2.2) 229 | zeitwerk (~> 2.6) 230 | rainbow (3.1.1) 231 | rake (13.2.1) 232 | rdoc (6.13.1) 233 | psych (>= 4.0.0) 234 | regexp_parser (2.10.0) 235 | reline (0.6.0) 236 | io-console (~> 0.5) 237 | rexml (3.4.1) 238 | rouge (4.5.1) 239 | rubocop (1.74.0) 240 | json (~> 2.3) 241 | language_server-protocol (~> 3.17.0.2) 242 | lint_roller (~> 1.1.0) 243 | parallel (~> 1.10) 244 | parser (>= 3.3.0.2) 245 | rainbow (>= 2.2.2, < 4.0) 246 | regexp_parser (>= 2.9.3, < 3.0) 247 | rubocop-ast (>= 1.38.0, < 2.0) 248 | ruby-progressbar (~> 1.7) 249 | unicode-display_width (>= 2.4.0, < 4.0) 250 | rubocop-ast (1.42.0) 251 | parser (>= 3.3.7.2) 252 | rubocop-md (2.0.0) 253 | lint_roller (~> 1.1) 254 | rubocop (>= 1.72.1) 255 | rubocop-minitest (0.37.1) 256 | lint_roller (~> 1.1) 257 | rubocop (>= 1.72.1, < 2.0) 258 | rubocop-ast (>= 1.38.0, < 2.0) 259 | rubocop-packaging (0.6.0) 260 | lint_roller (~> 1.1.0) 261 | rubocop (>= 1.72.1, < 2.0) 262 | rubocop-performance (1.24.0) 263 | lint_roller (~> 1.1) 264 | rubocop (>= 1.72.1, < 2.0) 265 | rubocop-ast (>= 1.38.0, < 2.0) 266 | rubocop-rails (2.30.3) 267 | activesupport (>= 4.2.0) 268 | lint_roller (~> 1.1) 269 | rack (>= 1.1) 270 | rubocop (>= 1.72.1, < 2.0) 271 | rubocop-ast (>= 1.38.0, < 2.0) 272 | rubocop-rails_config (1.17.0) 273 | rubocop (>= 1.72.2) 274 | rubocop-ast (>= 1.38.0) 275 | rubocop-md 276 | rubocop-minitest (~> 0.37) 277 | rubocop-packaging (~> 0.5) 278 | rubocop-performance (~> 1.24) 279 | rubocop-rails (~> 2.30) 280 | ruby-progressbar (1.13.0) 281 | ruby2html (1.6.6) 282 | htmlbeautifier (>= 1.4) 283 | securerandom (0.4.1) 284 | sirop (0.5) 285 | prism (~> 0.27.0) 286 | stringio (3.1.6) 287 | temple (0.10.3) 288 | thor (1.3.2) 289 | tilt (2.6.0) 290 | timeout (0.4.3) 291 | trailblazer-option (0.1.2) 292 | tzinfo (2.0.6) 293 | concurrent-ruby (~> 1.0) 294 | uber (0.1.0) 295 | unicode-display_width (3.1.4) 296 | unicode-emoji (~> 4.0, >= 4.0.4) 297 | unicode-emoji (4.0.4) 298 | uri (1.0.3) 299 | useragent (0.16.11) 300 | view_component (3.22.0) 301 | activesupport (>= 5.2.0, < 8.1) 302 | concurrent-ruby (= 1.3.4) 303 | method_source (~> 1.0) 304 | websocket-driver (0.7.7) 305 | base64 306 | websocket-extensions (>= 0.1.0) 307 | websocket-extensions (0.1.5) 308 | zeitwerk (2.7.2) 309 | 310 | PLATFORMS 311 | aarch64-linux 312 | arm64-darwin-21 313 | arm64-darwin-22 314 | arm64-darwin-23 315 | arm64-darwin-24 316 | x86_64-darwin-20 317 | x86_64-linux 318 | 319 | DEPENDENCIES 320 | benchmark-ips (~> 2.13) 321 | cells-erb 322 | cells-rails 323 | hanami-view 324 | papercraft 325 | phlex-rails 326 | pry 327 | rails (~> 8.0) 328 | rubocop-rails_config (~> 1.16) 329 | ruby2html 330 | view_component 331 | 332 | RUBY VERSION 333 | ruby 3.4.1p0 334 | 335 | BUNDLED WITH 336 | 2.5.11 337 | --------------------------------------------------------------------------------