phlex 0.2.1 → 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.
- checksums.yaml +4 -4
- data/.editorconfig +5 -0
- data/.rubocop.yml +6 -0
- data/CONTRIBUTING.md +23 -0
- data/README.md +2 -2
- data/Rakefile +2 -2
- data/SECURITY.md +5 -0
- data/bench.rb +1 -1
- data/docs/assets/application.css +2 -0
- data/docs/build.rb +4 -4
- data/docs/components/callout.rb +5 -5
- data/docs/components/code_block.rb +18 -18
- data/docs/components/example.rb +23 -23
- data/docs/components/heading.rb +5 -5
- data/docs/components/layout.rb +42 -41
- data/docs/components/markdown.rb +19 -19
- data/docs/components/tabs/tab.rb +18 -18
- data/docs/components/tabs.rb +21 -21
- data/docs/components/title.rb +5 -5
- data/docs/page_builder.rb +32 -32
- data/docs/pages/application_page.rb +3 -3
- data/docs/pages/index.rb +23 -25
- data/docs/pages/rails_integration.rb +58 -0
- data/docs/pages/templates.rb +238 -238
- data/docs/pages/views.rb +175 -0
- data/fixtures/compilation/vcall.rb +38 -0
- data/fixtures/dummy/app/views/articles/form.rb +9 -9
- data/fixtures/dummy/app/views/articles/index.html.erb +3 -0
- data/fixtures/dummy/app/views/card.rb +8 -8
- data/fixtures/dummy/app/views/heading.rb +9 -0
- data/fixtures/dummy/config/routes.rb +1 -1
- data/fixtures/dummy/db/schema.rb +2 -2
- data/fixtures/layout.rb +24 -24
- data/fixtures/page.rb +34 -34
- data/fixtures/test_helper.rb +2 -2
- data/fixtures/view_helper.rb +16 -0
- data/lib/generators/phlex/component/USAGE +1 -1
- data/lib/generators/phlex/component/component_generator.rb +8 -8
- data/lib/generators/phlex/component/templates/{component.rb.erb → view.rb.erb} +1 -1
- data/lib/install/phlex.rb +18 -0
- data/lib/overrides/symbol/name.rb +1 -1
- data/lib/phlex/block.rb +12 -12
- data/lib/phlex/buffered.rb +13 -13
- data/lib/phlex/compiler/formatter.rb +91 -0
- data/lib/phlex/compiler/generators/standard_element.rb +30 -0
- data/lib/phlex/compiler/generators/void_element.rb +29 -0
- data/lib/phlex/compiler/optimizers/base_optimizer.rb +34 -0
- data/lib/phlex/compiler/optimizers/vcall.rb +29 -0
- data/lib/phlex/compiler/visitors/base_visitor.rb +19 -0
- data/lib/phlex/compiler/visitors/component.rb +28 -0
- data/lib/phlex/compiler/visitors/component_method.rb +28 -0
- data/lib/phlex/compiler/visitors/file.rb +17 -0
- data/lib/phlex/compiler.rb +50 -0
- data/lib/phlex/configuration.rb +3 -3
- data/lib/phlex/engine.rb +11 -0
- data/lib/phlex/html.rb +136 -18
- data/lib/phlex/rails/tag_helpers.rb +23 -23
- data/lib/phlex/renderable.rb +33 -27
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex/view.rb +223 -0
- data/lib/phlex.rb +27 -12
- data/lib/tasks/phlex_tasks.rake +10 -0
- metadata +41 -10
- data/CHANGELOG.md +0 -5
- data/docs/pages/components.rb +0 -175
- data/fixtures/component_helper.rb +0 -16
- data/lib/phlex/component.rb +0 -191
- data/lib/phlex/rails.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4314dc80b5da3795454b1906f8c51b0b875fc6fea6962c2a77476dd6a4f01d34
|
4
|
+
data.tar.gz: 7ca6921fed9c3d2d6122dd0e77e2a97f5f100dc4a003d76c76c8dd407fddf86e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e7a837a1c2584e7c7f0c7955c2f49f48d4cbc049e19ba2cb707f6cfb3662edca2c05dc5cb80fc59ec6d50416fcc60894ec77bf3cc987cf22aab4ea9c470e95a
|
7
|
+
data.tar.gz: 1365cfd00e035b4364a66d89d3657e1d49745e1950d64b7b0fe9a17bcf27551d59447f6cd86451c3279886303cef119a69b8c752acddc81045b0eefc66fd5a4f
|
data/.editorconfig
ADDED
data/.rubocop.yml
CHANGED
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/README.md
CHANGED
@@ -16,9 +16,9 @@ Everyone interacting in Phlex codebases, issue trackers, chat rooms and mailing
|
|
16
16
|
|
17
17
|
### Sponsorship 💖
|
18
18
|
|
19
|
-
Maintaining a library
|
19
|
+
Maintaining a library is a lot of work. If your company benefits from this work or is likely to benefit from it in the future, please consider [sponsorship](https://github.com/sponsors/joeldrapper). Phlex is actively developed and maintained by **[Joel Drapper](https://github.com/sponsors/joeldrapper)**.
|
20
20
|
|
21
|
-
### Security
|
21
|
+
### Security 🚨
|
22
22
|
|
23
23
|
If you’ve found a potential security issue, please email [joel+security@drapper.me](mailto:joel+security@drapper.me).
|
24
24
|
|
data/Rakefile
CHANGED
data/SECURITY.md
ADDED
data/bench.rb
CHANGED
data/docs/assets/application.css
CHANGED
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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")
|
data/docs/components/callout.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
5
|
-
|
4
|
+
class CodeBlock < Phlex::View
|
5
|
+
FORMATTER = Rouge::Formatters::HTML.new
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
def initialize(code, syntax:)
|
8
|
+
@code = code
|
9
|
+
@syntax = syntax
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
20
|
+
private
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def lexer
|
23
|
+
Rouge::Lexer.find(@syntax)
|
24
|
+
end
|
25
|
+
end
|
26
26
|
end
|
data/docs/components/example.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class Example < Phlex::View
|
5
|
+
def initialize
|
6
|
+
@sandbox = Module.new
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
def template(&block)
|
10
|
+
render Tabs.new do |t|
|
11
|
+
@t = t
|
12
|
+
yield self
|
13
|
+
end
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def tab(name, code)
|
17
|
+
@t.tab(name) do
|
18
|
+
render CodeBlock.new(code, syntax: :ruby)
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
@sandbox.class_eval(code)
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
def execute(code)
|
25
|
+
output = @sandbox.class_eval(code)
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
@t.tab("HTML Output") do
|
28
|
+
render CodeBlock.new(HtmlBeautifier.beautify(output), syntax: :html)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
32
|
end
|
data/docs/components/heading.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
data/docs/components/layout.rb
CHANGED
@@ -1,45 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
data/docs/components/markdown.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
16
|
+
MARKDOWN = Redcarpet::Markdown.new(Render.new, autolink: true, tables: true)
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
def initialize(content)
|
19
|
+
@content = content
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def template
|
23
|
+
raw MARKDOWN.render(@content)
|
24
|
+
end
|
25
|
+
end
|
26
26
|
end
|
data/docs/components/tabs/tab.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
class Tabs
|
5
|
+
class Tab < Phlex::View
|
6
|
+
def initialize(name:, checked:)
|
7
|
+
@name = name
|
8
|
+
@checked = checked
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
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
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
def unique_identifier
|
22
|
+
@unique_identifier ||= SecureRandom.hex
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
26
|
end
|
data/docs/components/tabs.rb
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class Tabs < Phlex::View
|
5
|
+
def initialize
|
6
|
+
@index = 1
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def tab(name, &block)
|
16
|
+
render(Tab.new(name: name, checked: first?), &block)
|
17
|
+
@index += 1
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def unique_identifier
|
21
|
+
@unique_identifier ||= SecureRandom.hex
|
22
|
+
end
|
23
23
|
|
24
|
-
|
24
|
+
private
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
def first?
|
27
|
+
@index == 1
|
28
|
+
end
|
29
|
+
end
|
30
30
|
end
|
data/docs/components/title.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Components
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|