GithubHelp home page GithubHelp logo

phlex-ruby / phlex Goto Github PK

View Code? Open in Web Editor NEW
1.1K 19.0 75.0 996 KB

A framework for building object-oriented views in Ruby.

Home Page: https://www.phlex.fun

License: MIT License

Ruby 99.97% Shell 0.03%
ruby view-components ui-components phlex component-architecture component-library server-side-rendering ssr

phlex's Introduction

Phlex logo

Phlex lets you compose web views in pure Ruby — kind of like JSX, but not really anything like JSX. It’s super-fast, thread-safe and supports TruffleRuby v23.1+ and CRuby 3.2+. Phlex currently supports HTML and SVG views, and we’re exploring JSON and XML.

Docs and more at Phlex.fun

Maintenance schedule

Bug fixes

  • Only the latest minor version of each major version will receive bug fixes.
  • We may choose to fix bugs by releasing a new minor version rather than patching the existing minor version.
  • Major versions will stop receiving bug fixes one year after the next major version is released.
  • We are not likely to attempt to fix bugs that affect maintained versions of Ruby, even if our gemspec lists support for unmaintained versions of Ruby.

Security patches

  • When a security issue is brought to our attention, we aim to release patches for any minor version that was released in the last year.
  • Additionally, the latest minor version of the latest major version will receive security patches, even if that version is over a year old.

Prior Art 🎨

phlex's People

Contributors

alexandreruban avatar blocknotes avatar bradgessler avatar brandondrew avatar byroot avatar chrisseaton avatar davekaro avatar dependabot[bot] avatar djfpaagman avatar enderahmetyurt avatar fractaledmind avatar henrik avatar imella avatar jai-x avatar jaredcwhite avatar jochenseeber avatar joeldrapper avatar joelmoss avatar konnorrogers avatar lucianghinda avatar mansakondo avatar marcoroth avatar mbriggs avatar megatux avatar mkdynamic avatar nikolalsvk avatar olleolleolle avatar stephannv avatar sullyvannunes avatar willcosgrove avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phlex's Issues

"phlex/rails" is not being required

Phlex is meant to detect the presence of Rails and automatically require "phlex/rails". It doesn't look like this is working and unfortunately it's quite difficult to test because phlex/rails ends up being required by the test runner anyway.

Here's where it's meant to be required. https://github.com/joeldrapper/phlex/blob/0a6c3ab1d4cd2d6c72290d36081a38fd09c9ccb0/lib/phlex.rb#L28-L30

Workaround

For now, you can manually require "phlex/rails" from Rails apps using Phlex.

Don't use `Views` as the default component folder in Rails

After working on documenting the Rails generator for Phlex components in #178, I realized we still use the app/views folder as the default home for Phlex components.

I think it's not very practical, because app/views is already used for .html.erb templates in Rails.

Would it make sense to use something else? The View Components gem uses app/components by default. Maybe we could use app/phlex_components?

Better i18n support

Currently Phlex does not provide anything for i18n, one can include I18n and then use the translation helpers with absolute keys.

So while this works, adding the rails helpers for i18n will cause problems, when trying to use relative keys like t(".new"), it will raise an error, that there is no @virtual_path set.

ViewComponent solves this, by setting the @virtual_path in the render_in method.

ViewComponent also supports having the language files as sidecar files, which is pretty neat, and might be a cool thing for Phlex as well.

Some of my research on the topic can be found here: https://discord.com/channels/629472241427415060/1002702966551216139/1022976024432095373

Initial Rails integration documentation

  • Configuration in initialiser
  • Component generator
  • Rendering Phlex components in ERB templates
  • Rendering Rails partials in Phlex
  • Rendering ViewComponent components in Phlex
  • Using Rails helpers

Support custom elements

I'm excited to see how this library evolves! One thing I was wondering is the ability to support custom element tags (aka arbitrary tag names with at least one dash).

I worked on a PR for Papercraft to support converting Ruby calls like my_custom_element to my-custom-element and would be happy to contribute here if you have an approach in mind.

Emmet-style CSS classes

We should be able to add classes to a tag through method calls.

div.shadow.rounded.p_5(&)

undefined local variable or method `whitespace'

Hello,

I am not 100% this is a bug. It might be that I am not using right phex component.

But here is a short test

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  ruby "3.1.1"

  gem "phlex"
end

class LinksComponent < Phlex::Component
  def template
    a "Home", href: "/"
    whitespace
    a "About", href: "/about"
    whitespace
    a "Contact", href: "/contact"
  end
end

m = LinksComponent.new(name: "Twitter")
m.call

If I run the code above, I get the following error:

test-bug.rb:18:in `template': undefined local variable or method `whitespace' for #<LinksComponent:0x000000010b61f1a0 @name="Twitter", @_render_context=#<LinksComponent:0x000000010b61e9a8 @name="Twitter", @_render_context=#<LinksComponent:0x000000010b61f1a0 ...>, @_children=[#<Phlex::Tag::A:0x000000010b61ecf0 @attributes={:href=>["/"]}, @_children=[#<Phlex::Text:0x000000010b61e7f0 @content="Home">]>]>, @_children=[#<Phlex::Tag::A:0x000000010b61ecf0 @attributes={:href=>["/"]}, @_children=[#<Phlex::Text:0x000000010b61e7f0 @content="Home">]>]> (NameError)

    whitespace
    ^^^^^^^^^^
	from /.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/phlex-0.1.0/lib/phlex/component.rb:14:in `initialize'

I took the example from the documentation here: https://www.phlex.fun/templates/

Broken in Ruby 3.0.x

Not sure if you want to support 3.0.x (the gemspec says so though), but right now the code is broken:

SyntaxError:
  /Users/dennis/Code/phlex/spec/phlex/component_spec.rb:5: syntax error, unexpected ')', expecting local variable or method
    def template(&)
                  ^
  /Users/dennis/Code/phlex/spec/phlex/component_spec.rb:7: syntax error, unexpected `end'
    end
    ^~~
  /Users/dennis/Code/phlex/spec/phlex/component_spec.rb:10: class definition in method body
  class ArticleComponent < Phlex::Component
  ^~~~~~~~~~~~~~~~~~~~~~
  /Users/dennis/Code/phlex/spec/phlex/component_spec.rb:16: class definition in method body
  class Article
  ^~~~~~~~~~~~~
  /Users/dennis/Code/phlex/spec/phlex/component_spec.rb:441: syntax error, unexpected ')'
  ...mple) { component.new(article:) }
  ...                              ^
  /Users/dennis/Code/phlex/spec/phlex/component_spec.rb:457: syntax error, unexpected `end', expecting '}'
      end
      ^~~

Rails view helpers

There are a number of Rails view helpers which we will need to support in some way or another.

The helpers that produce strings should be fine to just include. But helpers that product markup will need to be handled carefully. I expect most of these could be shipped in the form of a small standard library or components for Rails.

In particular, we may need our own form component.

Fragment caching

The cache key should include

  • User provided resource
  • Ruby version
  • Phlex version
  • Content block definition checksum
  • Relevant ancestors checksums
  • Sub component checksums
  • Sub component checksums from the content block definition

New documentation site

The README isn’t a great home for our documentation. I also experimented with GitBook that’s not ideal either. It can’t live in the repo so folks would need to sign up for a GitBook account and be invited separately to make changes to the documentation.

So the new plan is to generate our documentation from Phlex components.

Collections pattern

Introduced experimentally in #125. If this is the direction we want to go, it will need tests and documentation.

Rails install generator

  • Create a Phlex initialiser for configuration
  • Configure Tailwind if installed
  • Configure autoload paths to include app #197

Lazy `tokens` evaluation

Phlex tokens use Symbols / Procs for the condition statements. This means there's no requirement to execute the condition statements at definition. We can save them in a constant somewhere and apply them later.

For this to work, tokens would have to return a Phlex::Tokens object that can be coerced into a hash with **.

ViewComponent integration tests

I think it’d be a good idea to have a few ViewComponent integration tests.

  • Render a ViewComponent component in a Phlex component
  • Render a Phlex component in a ViewComponent component

Shorter `component` method

It might be worth either renaming the component method or giving it a short alias. A few options that come to mind:

  1. comp, com or even c
  2. render or draw
  3. view

Cache CSS classes in the ClassCollector

The ClassCollector uses method_missing to pick up arbitrary CSS classes chained on element methods. Chances are, you're going to use the same CSS classes more than once, so I wonder if it might make sense to cache a method on the ClassCollector to avoid the missing_method call the next time that CSS class is used.

I tried to put together a PoC but it's tricky because you can't use things like send or self.class.define_method from a BasicObject.

One possible drawback is that this could lead to a memory leak if someone sends arbitrary classes (think UUID or something weird) using send from a component. We could keep a counter of cached CSS classes and make sure it doesn't grow too big.

Rename `content` method

I would like to allow people to use an expose the content method as a “slot” for their component. For example, you might want to define a component that has slots for title and content. For example, you could define a Card component with title and content slots:

class Card < Phlex::Component
  def template(&)
    article(&)
  end

  def title(text)
    h3 text, class: "title"
  end

  def content(&block)
    div class: "content", &block
  end
end

And this component could be used like this:

class Example < Phlex::Component
  def template
    render Card do |c|
      c.title "Hi"

      c.content do
        p "Hello!"
      end
    end
  end
end

Supporting this means renaming the content method defined here:

https://github.com/joeldrapper/phlex/blob/bb358e7942749d7834f679bb8c6e54115d5616cd/lib/phlex/component.rb#L58-L67

This method yields self to the block but also captures the output of that yield as text content if the buffer wasn't changed by the yield. It's difficult to know what to call it.

Block javascript protocol in links

If a rails app renders a link from user input, it might be possible for a user to run arbitrary JavaScript on some else's account by using the javascript: link protocol. To guard against this Cross Site Scripting (XSS) attack, we should strip javascript: from the beginning of the href attribute on <a> tags.

phlex [not found] error when generating a Rails controller

Apparently Phlex is interfering with the Rails controller generators. I haven't yet been able to reproduce yet.

Discussed in #235

Originally posted by klippas-rob October 7, 2022
I'm new to Rails so this is likely my mistake…

rails g controller cards

produces:

      create  app/controllers/cards_controller.rb
       error  phlex [not found]
      invoke  test_unit
      create    test/controllers/cards_controller_test.rb
      invoke  helper
      create    app/helpers/cards_helper.rb
      invoke    test_unit

Prior to this I installed phlex with:

bin/rails phlex:install

and in my Gemfile I have:

gem "phlex", "~> 0.3.1"

Rendering is slow when re-using a component

#!/usr/bin/env ruby

require "phlex"
require "benchmark/ips"

class NavComponent < Phlex::Component
  def template
    nav id: "main_nav" do
      ul do
        li { a "Home", href: "/" }
        li { a "About", href: "/about" }
        li { a "Contact", href: "/contact" }
      end
    end
  end
end

nav_component = NavComponent.new

Benchmark.ips do |x|
  x.report("NavComponent1") { NavComponent.new.call }
  x.report("NavComponent2") { nav_component.call }
end
% bundle exec bench/nav.rb
Warming up --------------------------------------
       NavComponent1     3.399k i/100ms
       NavComponent2    39.000  i/100ms
Calculating -------------------------------------
       NavComponent1     35.428k (± 0.9%) i/s -    180.147k in   5.085325s
       NavComponent2     95.413  (±16.8%) i/s -    468.000  in   5.037483s

Generator failed

I added gem 'phlex' to the Gemfile and ran bin/rails phlex:install.
I then ran

bin/rails g phlex:view Card

which resulted in:

Could not find generator 'phlex:view'.

Ruby 3.1.2
Rails 7.0.4

Conversion of underscores to dashes should be configurable

Currently, Phlex automatically converts underscores _ to dashes - in CSS class output. This is because, while dashes are pretty standard for CSS classes (e.g. Tailwind), they are not allowed in Ruby Symbols / method names. I think this is a good default, but you should be able to disable it with a configuration option.

The conversion happens here https://github.com/joeldrapper/phlex/blob/main/lib/phlex/tag.rb#L56

Nothing else in Phlex is configurable yet, so it'll be necessary to setup some kind of configuration pattern at the same time. The interface should be something like this, which you could add to an initialiser in Rails.

Phlex.configure do |config|
  config.convert_underscores_to_dashes = false
end

Rename `Phlex::Component` → `Phlex::View`

Phlex can be used to build layouts and pages as well as components. I think the word "component" it quite limiting. It refers to part of something but not the whole of something.

Rails Integration

Opening this issue as a reminder to consider interoperability with Rails partials — i.e. rendering an ERB partial inside a component and making it possible to render Phlex components in ERB templates.

Why?

  1. Interoperability with Rails ERB templates might make it easier to migrate a project to Phlex step-by-step.
  • Render Rails partials in a Phlex component
  • Render Phlex pages from Rails controller
  • Rails component helper to render components in Rails views
  • #75
  • #57
  • #120

Accessibility validations

We should be able to do some accessibility validations in development and testing environments, for example checking that id attributes are unique in the scope of each page.

Component text content

The first positional argument given to a component should be treated as its text content if no block is given and the component doesn't itself handle the positional argument.

Given this component

class HeaderComponent < Phlex::Component
  def template(&)
    h1.text_xl(&)
  end
end

We should be able to use it like this.

component HeaderComponent, "Hello World!"

Tailiwind classes won't load

Hey, thanks for working on this!, really excited about the possibilities of having full components on rails a la react :)

Found an issue with tailwind classes that i think has not been reported: Lets say im using a tailwind class in phlex that i haven't used before in a normal haml/erb file, it won't be defined in the browser, but if i use it in a html view it 'detects it'. It seems its due to compilation not being done when adding tailwind classes in phlex

Support .html.phlex files

If I’m understanding how Phlex works, in order to use it in Rails you will either need to call render in the controller and hand it a Phlex component, or embed a Phlex component in an erb, slim, or haml file. That got me thinking, what if Phlex supported .html.phlex files so that Phlex files could live in app/views. I’m thinking they’d be fairly similar to Jbuilder files then. They’d be anonymous components instead of named, and basically be what you see in the template functions in the examples.

You could support blocks with a yield, to nest templates (just like partials), and they’d have access to instance variables set in the controller.

Take this example from the documentation:

class NavComponent < Phlex::Component
  def template
    nav id: "main_nav" do
      ul do
        li { a "Home", href: "/" }
        li { a "About", href: "/about" }
        li { a "Contact", href: "/contact" }
      end
    end
  end
end

Now imagine this:

# app/views/things/show.html.phlex
nav id: "main_nav" do
  ul do
    @links.each do |link|
      li { a link.text, href: link.url }
    end
  end
end

I feel like it could help gain traction if people could still define views in a way they’re used to (app/views, with a DSL instead of defining a class). This would make it more of a direct competitor with ERB, Slim, and HAML then.

Under the hood, I imagine Phlex would define a constant for this based on the path. Like Phlex::RailsViews::Things::Show or something.

My thought is, if it’s a reusable thing, devs would give it it’s own name and put it in app/components or something. But if it’s just a view for a specific page, this anonymous form seems simpler, and more Railsy (and intentionally not easily reusable), but still defines the component with Ruby.

Magic frozen string comment breaks component

Hello,

I started migrating some ViewComponent files to Phlex but I am getting "can't modify frozen String" whenever I try to include a class attribute to an anchor tag.

# app/frontend/navbar/component.rb

# frozen_string_literal: true

module Navbar
  class Component < Phlex::Component
    def template
      nav.navbar.bg_base_100 do
        a("Get started", href: Current.rodauth.login_path, class: "btn")
      end
    end
  end
end

After checking out this file, I realized that I needed to remove the magic comment. I think it should be added in the README or are they any plans to support it?

Also, I think it would be nice to have a section to show how to embed a Phlex component in an erb template for starters. It's getting very late over here and It took me more than I am willing to admit to come up with: <%= render html: Navbar::Component.new.call.html_safe %> 😪 😂

Safe attribute keys

To add some extra protection against unsafe content, we should remove certain characters from attribute keys. We don’t need to do a full HTML escape because we don't need to preserve anything. Should probably gsub the following <, >, ", ', &.

It's already quite difficult to shoot yourself in the foot with this. You’d need to coerce a hash with unsafe keys into the arguments for a tag, e.g. div **unsafe_hash.

1.0 Documentation

Guide

  • Introduction
  • Views
  • Templates
  • Helpers

Standard library

  • Collections
  • Tables

Rails

  • Getting started
  • Rendering views
  • Layouts
  • Helpers
  • Migrating to Phlex

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.