phlex 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of phlex might be problematic. Click here for more details.

Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +5 -0
  3. data/.rubocop.yml +6 -0
  4. data/CONTRIBUTING.md +23 -0
  5. data/Rakefile +2 -2
  6. data/bench.rb +1 -1
  7. data/docs/assets/application.css +2 -0
  8. data/docs/build.rb +4 -4
  9. data/docs/components/callout.rb +5 -5
  10. data/docs/components/code_block.rb +18 -18
  11. data/docs/components/example.rb +23 -23
  12. data/docs/components/heading.rb +5 -5
  13. data/docs/components/layout.rb +42 -41
  14. data/docs/components/markdown.rb +19 -19
  15. data/docs/components/tabs/tab.rb +18 -18
  16. data/docs/components/tabs.rb +21 -21
  17. data/docs/components/title.rb +5 -5
  18. data/docs/page_builder.rb +32 -32
  19. data/docs/pages/application_page.rb +3 -3
  20. data/docs/pages/index.rb +23 -25
  21. data/docs/pages/rails_integration.rb +58 -0
  22. data/docs/pages/templates.rb +238 -238
  23. data/docs/pages/views.rb +175 -0
  24. data/fixtures/compilation/vcall.rb +38 -0
  25. data/fixtures/dummy/app/views/articles/form.rb +9 -9
  26. data/fixtures/dummy/app/views/card.rb +8 -8
  27. data/fixtures/dummy/app/views/heading.rb +5 -5
  28. data/fixtures/dummy/config/routes.rb +1 -1
  29. data/fixtures/dummy/db/schema.rb +2 -2
  30. data/fixtures/layout.rb +24 -24
  31. data/fixtures/page.rb +34 -34
  32. data/fixtures/test_helper.rb +2 -2
  33. data/fixtures/view_helper.rb +16 -0
  34. data/lib/generators/phlex/component/USAGE +1 -1
  35. data/lib/generators/phlex/component/component_generator.rb +8 -8
  36. data/lib/generators/phlex/component/templates/{component.rb.erb → view.rb.erb} +1 -1
  37. data/lib/install/phlex.rb +18 -0
  38. data/lib/overrides/symbol/name.rb +1 -1
  39. data/lib/phlex/block.rb +12 -12
  40. data/lib/phlex/buffered.rb +13 -13
  41. data/lib/phlex/compiler/formatter.rb +91 -0
  42. data/lib/phlex/compiler/generators/standard_element.rb +30 -0
  43. data/lib/phlex/compiler/generators/void_element.rb +29 -0
  44. data/lib/phlex/compiler/optimizers/base_optimizer.rb +34 -0
  45. data/lib/phlex/compiler/optimizers/vcall.rb +29 -0
  46. data/lib/phlex/compiler/visitors/base_visitor.rb +19 -0
  47. data/lib/phlex/compiler/visitors/component.rb +28 -0
  48. data/lib/phlex/compiler/visitors/component_method.rb +28 -0
  49. data/lib/phlex/compiler/visitors/file.rb +17 -0
  50. data/lib/phlex/compiler.rb +50 -0
  51. data/lib/phlex/configuration.rb +3 -3
  52. data/lib/phlex/engine.rb +11 -0
  53. data/lib/phlex/html.rb +128 -17
  54. data/lib/phlex/rails/tag_helpers.rb +23 -23
  55. data/lib/phlex/renderable.rb +32 -32
  56. data/lib/phlex/version.rb +1 -1
  57. data/lib/phlex/view.rb +223 -0
  58. data/lib/phlex.rb +27 -12
  59. data/lib/tasks/phlex_tasks.rake +10 -0
  60. metadata +39 -10
  61. data/CHANGELOG.md +0 -5
  62. data/docs/pages/components.rb +0 -175
  63. data/fixtures/component_helper.rb +0 -16
  64. data/lib/phlex/component.rb +0 -196
  65. data/lib/phlex/rails.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e96f6deb872d9da9c40cece7e93c195fc5f2f58dc1d61a2277318a290018d64
4
- data.tar.gz: 759f185fc168d0d98647ad71c589ec8d1425b153382c37a070eb28ca8015c69b
3
+ metadata.gz: 4314dc80b5da3795454b1906f8c51b0b875fc6fea6962c2a77476dd6a4f01d34
4
+ data.tar.gz: 7ca6921fed9c3d2d6122dd0e77e2a97f5f100dc4a003d76c76c8dd407fddf86e
5
5
  SHA512:
6
- metadata.gz: 96c765e8a02c0e79cef8d1ed44bac721844e0fd8ae3a7e921176be092775bb66a3cfd6e28a75b697eb8f1debd01c2738e55044fc49a834937c7c79c4220beebb
7
- data.tar.gz: b5d99002d25e7a426102b6d935fefe0b18145eb780409745a0c63e6523db3668a12ea896e2155e6d8e2b40acb106d4298a8c8d93fa785c3f44472c4667ad67a4
6
+ metadata.gz: 9e7a837a1c2584e7c7f0c7955c2f49f48d4cbc049e19ba2cb707f6cfb3662edca2c05dc5cb80fc59ec6d50416fcc60894ec77bf3cc987cf22aab4ea9c470e95a
7
+ data.tar.gz: 1365cfd00e035b4364a66d89d3657e1d49745e1950d64b7b0fe9a17bcf27551d59447f6cd86451c3279886303cef119a69b8c752acddc81045b0eefc66fd5a4f
data/.editorconfig ADDED
@@ -0,0 +1,5 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = tab
5
+ indent_size = 2
data/.rubocop.yml CHANGED
@@ -2,3 +2,9 @@ inherit_from: "https://www.goodcop.style"
2
2
 
3
3
  AllCops:
4
4
  TargetRubyVersion: 2.7
5
+
6
+ Layout/IndentationStyle:
7
+ EnforcedStyle: tabs
8
+
9
+ Layout/IndentationWidth:
10
+ Width: 1
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,23 @@
1
+ # Contributing to Phlex
2
+
3
+ ## Priorities
4
+
5
+ We’re trying to provide the best possible developer experience for the people using Phlex in their apps and the best possible performance for the users of those apps.
6
+
7
+ Phlex is incredibly complex and requires a lot of meta-programming but when you use it, it feels simple. Phlex views feel like plain old Ruby objects. You just subclass and define a couple of methods. That’s it. That’s how it should be.
8
+
9
+ ## Setup
10
+
11
+ - Install dependencies `bundle install`
12
+ - Run the tests `bundle exec sus`
13
+ - Run Rubocop and auto-correct `bundle exec rubocop -A`
14
+
15
+ ## Tests
16
+
17
+ We use the **[Sus](https://github.com/ioquatix/sus)** testing framework. It feels a bit like RSpec with a few small differences. There’s no documentation for Sus at the moment, but you should be able to pick up the basics from reading other tests or looking at the implementation in the Sus repo.
18
+
19
+ You can run all the tests with `bundle exec sus`.
20
+
21
+ ## Documentation
22
+
23
+ Documentation is deployed when it’s merged into the `latest` branch with a release. But you can build and preview the docs locally by running `bin/docs`.
data/Rakefile CHANGED
@@ -3,8 +3,8 @@
3
3
  require "bundler/gem_tasks"
4
4
 
5
5
  begin
6
- require "rspec/core/rake_task"
7
- RSpec::Core::RakeTask.new(:spec)
6
+ require "rspec/core/rake_task"
7
+ RSpec::Core::RakeTask.new(:spec)
8
8
  end
9
9
 
10
10
  task default: :spec
data/bench.rb CHANGED
@@ -10,5 +10,5 @@ require_relative "fixtures/layout"
10
10
  puts RUBY_DESCRIPTION
11
11
 
12
12
  Benchmark.ips do |x|
13
- x.report("Page") { Example::Page.new.call }
13
+ x.report("Page") { Example::Page.new.call }
14
14
  end
@@ -2,6 +2,8 @@
2
2
  @tailwind components;
3
3
  @tailwind utilities;
4
4
 
5
+ pre { tab-size: 2; }
6
+
5
7
  .tabs input[type="radio"]:checked + label {
6
8
  background: #f8f8f8;
7
9
  z-index: 1;
data/docs/build.rb CHANGED
@@ -8,10 +8,10 @@ require "fileutils"
8
8
  Bundler.require :docs
9
9
 
10
10
  Zeitwerk::Loader.new.tap do |loader|
11
- loader.push_dir(__dir__)
12
- loader.ignore(__FILE__)
13
- loader.setup
14
- loader.eager_load
11
+ loader.push_dir(__dir__)
12
+ loader.ignore(__FILE__)
13
+ loader.setup
14
+ loader.eager_load
15
15
  end
16
16
 
17
17
  FileUtils.mkdir_p("#{__dir__}/dist")
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Callout < Phlex::Component
5
- def template(&block)
6
- div(class: "rounded bg-orange-50 text-sm p-5 border border-orange-100", &block)
7
- end
8
- end
4
+ class Callout < Phlex::View
5
+ def template(&block)
6
+ div(class: "rounded bg-orange-50 text-sm p-5 border border-orange-100", &block)
7
+ end
8
+ end
9
9
  end
@@ -1,26 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class CodeBlock < Phlex::Component
5
- FORMATTER = Rouge::Formatters::HTML.new
4
+ class CodeBlock < Phlex::View
5
+ FORMATTER = Rouge::Formatters::HTML.new
6
6
 
7
- def initialize(code, syntax:)
8
- @code = code
9
- @syntax = syntax
10
- end
7
+ def initialize(code, syntax:)
8
+ @code = code
9
+ @syntax = syntax
10
+ end
11
11
 
12
- def template
13
- pre(class: "highlight p-5 whitespace-pre-wrap bg-stone-50") {
14
- raw FORMATTER.format(
15
- lexer.lex(@code)
16
- )
17
- }
18
- end
12
+ def template
13
+ pre(class: "highlight p-5 whitespace-pre-wrap bg-stone-50") {
14
+ raw FORMATTER.format(
15
+ lexer.lex(@code)
16
+ )
17
+ }
18
+ end
19
19
 
20
- private
20
+ private
21
21
 
22
- def lexer
23
- Rouge::Lexer.find(@syntax)
24
- end
25
- end
22
+ def lexer
23
+ Rouge::Lexer.find(@syntax)
24
+ end
25
+ end
26
26
  end
@@ -1,32 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Example < Phlex::Component
5
- def initialize
6
- @sandbox = Module.new
7
- end
4
+ class Example < Phlex::View
5
+ def initialize
6
+ @sandbox = Module.new
7
+ end
8
8
 
9
- def template(&block)
10
- render Tabs.new do |t|
11
- @t = t
12
- yield self
13
- end
14
- end
9
+ def template(&block)
10
+ render Tabs.new do |t|
11
+ @t = t
12
+ yield self
13
+ end
14
+ end
15
15
 
16
- def tab(name, code)
17
- @t.tab(name) do
18
- render CodeBlock.new(code, syntax: :ruby)
19
- end
16
+ def tab(name, code)
17
+ @t.tab(name) do
18
+ render CodeBlock.new(code, syntax: :ruby)
19
+ end
20
20
 
21
- @sandbox.instance_eval(code)
22
- end
21
+ @sandbox.class_eval(code)
22
+ end
23
23
 
24
- def execute(code)
25
- output = @sandbox.instance_eval(code)
24
+ def execute(code)
25
+ output = @sandbox.class_eval(code)
26
26
 
27
- @t.tab("HTML Output") do
28
- render CodeBlock.new(HtmlBeautifier.beautify(output), syntax: :html)
29
- end
30
- end
31
- end
27
+ @t.tab("HTML Output") do
28
+ render CodeBlock.new(HtmlBeautifier.beautify(output), syntax: :html)
29
+ end
30
+ end
31
+ end
32
32
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Heading < Phlex::Component
5
- def template(&block)
6
- h2(class: "text-xl font-bold mt-10 mb-5", &block)
7
- end
8
- end
4
+ class Heading < Phlex::View
5
+ def template(&block)
6
+ h2(class: "text-xl font-bold mt-10 mb-5", &block)
7
+ end
8
+ end
9
9
  end
@@ -1,45 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Layout < Phlex::Component
5
- register_element :style
6
-
7
- def initialize(title:)
8
- @title = title
9
- end
10
-
11
- def template(&block)
12
- doctype
13
-
14
- html do
15
- head do
16
- meta charset: "utf-8"
17
- title @title
18
- link href: "/application.css", rel: "stylesheet"
19
- style { raw Rouge::Theme.find("github").render(scope: ".highlight") }
20
- end
21
-
22
- body class: "p-12" do
23
- div class: "max-w-screen-lg mx-auto grid grid-cols-4 gap-10" do
24
- header class: "col-span-1" do
25
- a(href: "/", class: "block") { img src: "/assets/logo.png", width: "150" }
26
-
27
- nav do
28
- ul do
29
- li { a "Introduction", href: "/" }
30
- li { a "Templates", href: "/templates" }
31
- li { a "Components", href: "/components" }
32
- li { a "Source code", href: "https://github.com/joeldrapper/phlex" }
33
- end
34
- end
35
- end
36
-
37
- main(class: "col-span-3", &block)
38
-
39
- footer class: "text-sm text-right col-span-4 py-10"
40
- end
41
- end
42
- end
43
- end
44
- end
4
+ class Layout < Phlex::View
5
+ register_element :style
6
+
7
+ def initialize(title:)
8
+ @title = title
9
+ end
10
+
11
+ def template(&block)
12
+ doctype
13
+
14
+ html do
15
+ head do
16
+ meta charset: "utf-8"
17
+ title @title
18
+ link href: "/application.css", rel: "stylesheet"
19
+ style { raw Rouge::Theme.find("github").render(scope: ".highlight") }
20
+ end
21
+
22
+ body class: "p-12" do
23
+ div class: "max-w-screen-lg mx-auto grid grid-cols-4 gap-10" do
24
+ header class: "col-span-1" do
25
+ a(href: "/", class: "block") { img src: "/assets/logo.png", width: "150" }
26
+
27
+ nav do
28
+ ul do
29
+ li { a "Introduction", href: "/" }
30
+ li { a "Templates", href: "/templates" }
31
+ li { a "Views", href: "/views" }
32
+ li { a "Rails integration", href: "/rails-integration" }
33
+ li { a "Source code", href: "https://github.com/joeldrapper/phlex" }
34
+ end
35
+ end
36
+ end
37
+
38
+ main(class: "col-span-3", &block)
39
+
40
+ footer class: "text-sm text-right col-span-4 py-10"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
45
46
  end
@@ -1,26 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Markdown < Phlex::Component
5
- class Render < Redcarpet::Render::HTML
6
- def header(text, level)
7
- case level
8
- when 1
9
- Title.new.call { text }
10
- else
11
- Heading.new.call { text }
12
- end
13
- end
14
- end
4
+ class Markdown < Phlex::View
5
+ class Render < Redcarpet::Render::HTML
6
+ def header(text, level)
7
+ case level
8
+ when 1
9
+ Title.new.call { text }
10
+ else
11
+ Heading.new.call { text }
12
+ end
13
+ end
14
+ end
15
15
 
16
- MARKDOWN = Redcarpet::Markdown.new(Render.new, autolink: true, tables: true)
16
+ MARKDOWN = Redcarpet::Markdown.new(Render.new, autolink: true, tables: true)
17
17
 
18
- def initialize(content)
19
- @content = content
20
- end
18
+ def initialize(content)
19
+ @content = content
20
+ end
21
21
 
22
- def template
23
- raw MARKDOWN.render(@content)
24
- end
25
- end
22
+ def template
23
+ raw MARKDOWN.render(@content)
24
+ end
25
+ end
26
26
  end
@@ -1,26 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Tabs
5
- class Tab < Phlex::Component
6
- def initialize(name:, checked:)
7
- @name = name
8
- @checked = checked
9
- end
4
+ class Tabs
5
+ class Tab < Phlex::View
6
+ def initialize(name:, checked:)
7
+ @name = name
8
+ @checked = checked
9
+ end
10
10
 
11
- def template(&block)
12
- input class: "opacity-0 fixed peer", type: "radio", name: @_parent.unique_identifier, id: unique_identifier, checked: @checked
11
+ def template(&block)
12
+ input class: "opacity-0 fixed peer", type: "radio", name: @_parent.unique_identifier, id: unique_identifier, checked: @checked
13
13
 
14
- label @name, id: "#{unique_identifier}-label", for: unique_identifier, role: "tab", aria_controls: "#{unique_identifier}-panel", class: "order-1 py-2 px-5 bg-white text-sm border border-b-0 border-l-0 font-medium first-of-type:border-l first-of-type:rounded-tl last-of-type:rounded-tr before:absolute before:pointer-events-none before:w-full before:ring before:h-full before:left-0 before:top-0 before:hidden before:rounded peer-focus:before:block cursor-pointer"
14
+ label @name, id: "#{unique_identifier}-label", for: unique_identifier, role: "tab", aria_controls: "#{unique_identifier}-panel", class: "order-1 py-2 px-5 bg-white text-sm border border-b-0 border-l-0 font-medium first-of-type:border-l first-of-type:rounded-tl last-of-type:rounded-tr before:absolute before:pointer-events-none before:w-full before:ring before:h-full before:left-0 before:top-0 before:hidden before:rounded peer-focus:before:block cursor-pointer"
15
15
 
16
- div id: "#{unique_identifier}-panel", role: "tabpanel", aria_labelledby: "#{unique_identifier}-label", class: "tab hidden order-2 w-full border rounded-b rounded-tr overflow-hidden" do
17
- @_parent.instance_exec(&block)
18
- end
19
- end
16
+ div id: "#{unique_identifier}-panel", role: "tabpanel", aria_labelledby: "#{unique_identifier}-label", class: "tab hidden order-2 w-full border rounded-b rounded-tr overflow-hidden" do
17
+ @_parent.instance_exec(&block)
18
+ end
19
+ end
20
20
 
21
- def unique_identifier
22
- @unique_identifier ||= SecureRandom.hex
23
- end
24
- end
25
- end
21
+ def unique_identifier
22
+ @unique_identifier ||= SecureRandom.hex
23
+ end
24
+ end
25
+ end
26
26
  end
@@ -1,30 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Tabs < Phlex::Component
5
- def initialize
6
- @index = 1
7
- end
4
+ class Tabs < Phlex::View
5
+ def initialize
6
+ @index = 1
7
+ end
8
8
 
9
- def template(&block)
10
- div class: "tabs flex flex-wrap relative my-5", role: "tablist" do
11
- content(&block)
12
- end
13
- end
9
+ def template(&block)
10
+ div class: "tabs flex flex-wrap relative my-5", role: "tablist" do
11
+ yield_content(&block)
12
+ end
13
+ end
14
14
 
15
- def tab(name, &block)
16
- render(Tab.new(name: name, checked: first?), &block)
17
- @index += 1
18
- end
15
+ def tab(name, &block)
16
+ render(Tab.new(name: name, checked: first?), &block)
17
+ @index += 1
18
+ end
19
19
 
20
- def unique_identifier
21
- @unique_identifier ||= SecureRandom.hex
22
- end
20
+ def unique_identifier
21
+ @unique_identifier ||= SecureRandom.hex
22
+ end
23
23
 
24
- private
24
+ private
25
25
 
26
- def first?
27
- @index == 1
28
- end
29
- end
26
+ def first?
27
+ @index == 1
28
+ end
29
+ end
30
30
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Components
4
- class Title < Phlex::Component
5
- def template(&block)
6
- h1(class: "text-2xl font-bold my-5", &block)
7
- end
8
- end
4
+ class Title < Phlex::View
5
+ def template(&block)
6
+ h1(class: "text-2xl font-bold my-5", &block)
7
+ end
8
+ end
9
9
  end
data/docs/page_builder.rb CHANGED
@@ -1,36 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class PageBuilder
4
- ROOT = Pages::ApplicationPage
5
-
6
- def self.build_all
7
- ROOT.subclasses.each { |page| new(page).call }
8
- end
9
-
10
- def initialize(page)
11
- @page = page
12
- end
13
-
14
- def call
15
- FileUtils.mkdir_p(directory)
16
- File.write(file, @page.new.call)
17
- end
18
-
19
- private
20
-
21
- def file
22
- "#{directory}/index.html"
23
- end
24
-
25
- def directory
26
- if path == "index"
27
- "#{__dir__}/dist"
28
- else
29
- "#{__dir__}/dist/#{path}"
30
- end
31
- end
32
-
33
- def path
34
- @page.name.downcase.split("::")[1..].join("/")
35
- end
4
+ ROOT = Pages::ApplicationPage
5
+
6
+ def self.build_all
7
+ ROOT.subclasses.each { |page| new(page).call }
8
+ end
9
+
10
+ def initialize(page)
11
+ @page = page
12
+ end
13
+
14
+ def call
15
+ FileUtils.mkdir_p(directory)
16
+ File.write(file, @page.new.call)
17
+ end
18
+
19
+ private
20
+
21
+ def file
22
+ "#{directory}/index.html"
23
+ end
24
+
25
+ def directory
26
+ if path == "index"
27
+ "#{__dir__}/dist"
28
+ else
29
+ "#{__dir__}/dist/#{path}"
30
+ end
31
+ end
32
+
33
+ def path
34
+ @page.name.split("::")[1..].map { _1.gsub(/(.)([A-Z])/, '\1-\2') }.map(&:downcase).join("/")
35
+ end
36
36
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pages
4
- class ApplicationPage < Phlex::Component
5
- include ::Components
6
- end
4
+ class ApplicationPage < Phlex::View
5
+ include ::Components
6
+ end
7
7
  end
data/docs/pages/index.rb CHANGED
@@ -1,40 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pages
4
- class Index < ApplicationPage
5
- def template
6
- render Layout.new(title: "Introduction to Phlex") do
7
- render Markdown.new(<<~MD)
8
- # Introduction
4
+ class Index < ApplicationPage
5
+ def template
6
+ render Layout.new(title: "Introduction to Phlex") do
7
+ render Markdown.new(<<~MD)
8
+ # Introduction
9
9
 
10
- Phlex is a framework for building fast, reusable, testable view components in pure Ruby.
10
+ Phlex is a framework for building fast, reusable, testable views in pure Ruby.
11
11
 
12
- Think of a component as anything on a web page that you could draw a circle around and name. Starting on the outside, you have the _“page”_ itself, inside the page is a _“header”_, in the header is a _“nav bar”_ and in the nav bar is a _“nav bar item.”_ These objects together make up an entire page and we call the objects components.
12
+ Each view object is an instance of a specific class of view. The nav-bar, for example, might contain three different nav-bar-items, but they’re all instances of the nav-bar-item class. This class, then, manifests everything there is to know about nav bar items in general. It models:
13
13
 
14
- Each component object is an instance of a specific class of component. The nav-bar, for example, might contain three different nav-bar-items, but they’re all instances of the nav-bar-item class. This class, then, manifests everything there is to know about nav bar items in general. It models:
14
+ 1. the **data** attributes being represented perhaps url and label;
15
+ 2. a **template**, which dictates how the data should be represented with HTML markup and CSS classes; and
16
+ 3. **logic**, conditions, or calculations on the data — perhaps a predicate method to determine whether the link is active or not.
15
17
 
16
- 1. the **data** attributes being represented — perhaps url and label;
17
- 2. a **template**, which dictates how the data should be represented with HTML markup and CSS classes; and
18
- 3. **logic**, conditions, or calculations on the data — perhaps a predicate method to determine whether the link is active or not.
18
+ # Why use Phlex?
19
19
 
20
- # Why use Phlex?
20
+ ## Better developer experience 🎉
21
21
 
22
- ## Better developer experience 🎉
22
+ You don’t need to introduce a new language like Slim, HAML, or ERB. Phlex views are plain old Ruby objects: view classes are just Ruby classes, templates are just methods, and HTML tags are just method calls. If you know how to define a method that calls another method, you pretty much already know how to use Phlex.
23
23
 
24
- You don’t need to introduce a new language like Slim, HAML, or ERB. Phlex components are plain old Ruby objects: component classes are just Ruby classes, templates are just methods, and HTML tags are just method calls. If you know how to define a method that calls another method, you pretty much already know how to use Phlex.
24
+ ## Better safety 🥽
25
+ Rails partials can implicitly depend on instance variables from a controller or view. This happens more often than you might think when copying code into a new partial extraction. If the partial is then rendered in a different context or the instance variable’s meaning changes, things can break quite severely without warning.
25
26
 
26
- ## Better safety 🥽
27
- Rails partials can implicitly depend on instance variables from a controller or view. This happens more often than you might think when copying code into a new partial extraction. If the partial is then rendered in a different context or the instance variable’s meaning changes, things can break quite severely without warning.
27
+ Conversely, Phlex view templates render in an isolated execution context where only the instance variables and methods for the specific view are exposed.
28
28
 
29
- Conversely, Phlex component templates render in an isolated execution context where only the instance variables and methods for the specific component are exposed.
29
+ ## Better performance 🚀
30
30
 
31
- ## Better performance 🚀
31
+ Phlex is ~4.35× faster than ActionView and ~2× faster than ViewComponent. Phlex views are also streamable.
32
32
 
33
- Phlex is ~4.35× faster than ActionView and ~2× faster than ViewComponent. Phlex components are also streamable.
34
-
35
- Rails apps typically spend 40-80% of the response time rendering views, so this could be a significant factor in overall app performance.
36
- MD
37
- end
38
- end
39
- end
33
+ Rails apps typically spend 40-80% of the response time rendering views, so this could be a significant factor in overall app performance.
34
+ MD
35
+ end
36
+ end
37
+ end
40
38
  end