Code Monkey home page Code Monkey logo

superglue's Introduction

Logo

Superglue

Use classic Rails to build rich React Redux applications with NO APIs and NO client-side routing.

Build Status

Superglue makes React and Redux as productive as Hotwire, Turbo and Stimulus. Its inspired by Turbolinks and designed to feel like a natural extension of Rails. Enjoy the benefits of Redux state management and React components without giving up the productivity of Rails form helpers, UJS, tag helpers, flash, cookie auth, and more.

Caution

This project is in its early phases of development. Its interface, behavior, and name are likely to change drastically before a major version release.

No APIs

Instead of APIs, Superglue leans on Rail's ability to respond to different mime types on the same route. In a Superglue application, if you direct your browser to /dashboard.html, you would see the HTML version of the content, and if you went to /dashboard.json you would see the JSON version of the exact same content down to the footer.

The end result would be something like this:

No Apis

Powered by Classic Rails

Superglue leans on Rails. Features like the flash, cookie auth, and URL helpers continue to be useful. Here's a look at the directory structure of a typical Rails application with Superglue.

app/
|-- controllers/
|-- views/
|   |-- dashboard/
|   |   |-- index.js # The React page component
|   |   |-- index.json.props # The json for the page component
|   |   |-- index.html.erb

PropsTemplate

Powering the JSON responses is PropsTemplate, a digable JSON templating DSL inspired by JBuilder. With PropsTemplate you can specify a path of the node you want, and PropsTemplate will walk the tree to it, skipping the execution of nodes that don't match the keypath.

No Apis

All together now!

Superglue comes with batteries that bring all the above concepts together to make building popular SPA features easy, painless, and productive.

SPA Navigation

A popular ask of SPAs is page-to-page navigation without reloading. This is easily done with Superglue's own UJS attributes inspired by Turbolinks:

  <a href='/posts' data-sg-visit />

The above will request for /posts with an accept of application/json, and when the client receives the response, swap out the current component for the component the response asks for, and pushState on history.

Easy Partial updates

Some features rely on updating some parts of the existing page. Imagine implementing type-ahead search. In traditional applications, you may need a new controller, routes, a discussion over versioning, JSON serializer, plenty of new JS code, etc.

haircuts

With Superglue, this can be done with a simple onChange

const onChange = (e) => (
  remote(`/dashboard?qry=${e.target.value}&props_at=data.header.search`)}
)

?> remote and visit is a thunk passed to every page component.

With props_at, the above will make a request to /dashboard?qry=haircut, dig your template for the data.header.search node, return it in the response, and immutably graft it in the exact same path on the redux store before finally letting React re-render.

For more on what you can do, check out our documentation.

Server-Side Rendering

Server-Side Rendering is supported via Humid. See the documentation for server-side rendering.

Documentation

Documentation is hosted on Github pages.

Contributing

Thank you, contributors!

Special Thanks

Thanks to jbuilder, scour, turbolinks3, turbograft, turbostreamer

superglue's People

Contributors

jho406 avatar rafe avatar whitecl 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

superglue's Issues

Mark fragments as experimental

Fragments in Superglue is a simple way to help identify and mutate cross cutting concerns.

For example, a shared header, if marked as a fragment, will get updated across pages in the cache automatically, if that page header changes. This means that today, a fragment is duplicated across all pages, and updating is a process of iterating through the cache and making changes.

I haven’t used this feature extensively yet, and i’m unsure of its effectiveness. There may also be better ways of modeling this in redux. Maybe a fragment node somewhere in the tree and having a selector that takes the fragment and insert it in the page state. This way, we only have one source of truth. That would be nice, but that also conflicts with digging since we need the complete tree to mutate the page state.

Anyway, I think its worth marking it in the documentation as a experimental feature.

Add React Native Support

Today, Rails has Strada, a cordova-ish solution that brings the web to mobile by using web views and a JS bridge to native functionality. Superglue can offer a better alternative. Imagine being able to use standard Rails helpers (the flash, form helpers) and your boring Rails workflow to build React Native applications, all while having 100% Native UI controls and access to react-native libraries.

In other words, while Strada tries to bring the Web to native, Superglue tries to bring just Rails to native.

Thoughts

The movement towards "pages"

expo and other JS frameworks are making their way towards page based development. Since Superglue is about page state over the wire. Is there some compatibility here that is worth investigating?

Known quirks of React Native

ReactNative is not the web. Specifically

  1. ReactNative fetch is NOT the web fetch, and there are known issues with redirects losing cookies
  2. Cookies have more known issues. There is a react-native-cookies package that might help this issue.

Possible solutions:

  1. Polyfill to the webview’s fetch. Its possible to create an instance of the Webview, and using injected javascript, create a polyfill fetch (fork something like https://github.com/developit/unfetch ) that proxys to the Webview.
  2. Embrace the known Quirks. React native isn’t quite the web, so create helpers in superglue_rails that give Rails more superpowers. For example, we can embrace that React has an issue with redirect by never redirecting, instead we can render a payload for the app to navigate to somewhere else.
class PostsController < ApplicationController
  def create
    ... do stuff
    navigate_to_screen :home_screen
  end
end

There might be new helpers to think about, for example: navigate_back. link helpersURL helpers.

Embracing React Native also means that some features of superglue would need to be disabled while on native. For example, UJS helpers. We may also need to reshape the redux state shape to accommodate the new navigation helpers.

Next Steps

Of the solutions, I am leaning towards embracing React Native’s quirks. While I don’t have a thought out comparison, I think that because React Native isn’t quite web, its simpler to embrace to quirkiness then to add a layered solution. I believe that Rails itself can be a “Be productive anywhere” solution, even without the web.

Add a ESLint exception

The initial state generated by superglue_rails is already properly escaped json and can be marked as html_safe. But erblint may call that line out and recommend calling to_json before html_safe. We could add something like the below, or add some documentation on how to use with ERB lint.

<script type="text/javascript">
  window.SUPERGLUE_INITIAL_PAGE_STATE=<%= initial_state.html_safe %>;<%# erblint:disable ErbSafety %>
</script>

Hide the `?_breezy_filter=key.path.to.node` API.

Fetching nodes requires the use a keypath in a url param on links and form elements, for example:

<a href="/?_breezy_filter=key.path.to.node.in.tree" data-bz-remote> Click to refresh this part of the UI </a>

I would rather not require the use of a parameter thats sort of private. How about something like this:

<a data-bz-remote="key.path.to.node.in.tree"> Click to refresh this part of the UI </a>

or this:

<a data-bz-remote data-bz-remote-node="key.path.to.node.in.tree"> Click to refresh this part of the UI </a>

Form helpers for Superglue

Rail's form helpers are an incredible way to quickly build forms from backend objects. I'd like to have some parity on the superglue side so quickly build uncontrolled components on the React side without using RailsTag

Something like this:

json.form_props form_with_props(
  url: onboarding_income_index_path,
  model: current_user.profile,
  method: :post,
  scope: :profile, local: true) do |f|

  f.number_field :email, max_length: 5
  f.number_field :password
  f.number_field :password_confirmation
end
   {
     .....
     email: {
       input: { type: "email",  maxLength: 5}
      }
   }

then on the Javascript side:

<form {...formProps}>
  <label {...formProps.email.label} />
  <input {...formProps.email.input}/>

  <label {...formProps.password.label}>
  <input {...formProps.password.input}>

  <RailsForm.Field {...formProps.passwordConfirmation} />

  <input type="submit" />
</RailsForm>

We leave how to structure the form up to the user so they can do this if they want in only html:

<label {...formProps.email.label}>
  <input {...formProps.email.input}/>
</label>

Convert Superglue JS to typescript

All of superglue is written in ES6. For developers working with React, typescript has been a valuable tool to have. It would improve the developer experience if superglue was converted to Typescript.

Thoughts

Not all features of superglue can be converted to typescript.

  1. digging is a feature that make it easy to create medium effort interactivity, but it works off of key paths (http://xyz.com/posts/?props_at=header.notifications)
  2. Superglue also comes with UJS helpers, for example: using placeholders. Not sure what the best way to type this is.

Regardless, typing what we can is still a great step forward.

Improve release scripts

The release scripts on superglue are pretty simple, but I wish there was a more prompty way of releasing. I tend to forget which scripts to run first, and if i accidentally forget an argument, I end up with mistagged packages.

Modify Generators to output TS files

This is in the same vein as #43 . When moving to typescript, we also want to update the generators to output ts files.

Thoughts:

The generators are located in this folder. There are a total of 4 files there that needs to be converted (edit.js, index.js, new.js, show.js).

Make superglue's gemspec more conservative

I think making superglue’s gemspec a bit more conservative might make sense. Right now it uses.

  s.add_dependency "actionpack", ">= 7.0.0"
  s.add_development_dependency "activerecord", ">= 7.0"

Which says that it works with all future version of Rails. This isn’t 100% true as there’s a few monkey patches in `superglue_rails` that may not be compatible with future Rails versions.

We could

  1. From the work to https://trello.com/c/SwB4ts3h/2-migrate-superglue-from-circleci-to-github test agains rails main weekly. Tho this is useful to do regardless of the change.
  2. Be a bit more explicit and twiddle-wakka the versions.

Add a `--webpack` option to the `rails g breezy:install` and `rails g breezy:view` generators.

Currently, the generators assumes a typical Rails setup with sprockets as the main asset packaging mechanism. All JS boilerplate gets tossed inside app/assets/... and uses ES5.

Rails 5 users have the option of using webpack which uses the app/javascript directory instead of app/assets/.... I'd like to add a --webpack option to all generators to produce ES6 boilerplate that goes into app/javascript.

Acceptance

  1. When running rails g breezy:install --webpack all existing javascript boilerplate should go in app/javascripts. See this as an example: https://github.com/jho406/kitchen/tree/master/app/javascript
  1. When running rails g breezy:view XYZ someaction --webpack all javascript boilerplate for the generator should go in app/javascripts. For example, https://github.com/jho406/kitchen/tree/master/app/javascript/views. Like the above, it should be in ES6.

  2. Leave the boilerplate that produces app/views/layouts/application.html.erb the way it is. I don't think there's a need to change that.

Add support for multifetch in BreezyTemplates

BreezyTemplate is a sibling/fork of JBuilder, but due to the behavior of how set! works in Breezy (it doesn't merge, it overrides), it should be possible to implement multifetch caches pretty easily.

When using caching like this:

json.author(cache: ["some_cache_key"]) do
  json.first_name "tommy"
end

BreezyTemplates stores a javascript string like this in the Caching backend:

"Breezy.cache("some_cache_key", {first_name: "tommy"});"

Implementing multi-fetch should be just a concat on values fetched from redis. As opposed to JBuilder which returns Ruby from Redis and then merged with other values. See rails/jbuilder#289 for more information.

Convert to ES6

Breezy was forked from Turbolinks 3, which was using coffeescript. I would like to convert the entire project to ES6.

Remove the need for a blank foobar.html.erb

A Superglue app’s directory structure looks like this:

app/
|-- controllers/
|-- views/
|   |-- dashboard/
|   |   |-- index.js # The React page component
|   |   |-- index.json.props # The json for the page component
|   |   |-- index.html.erb

Almost always index.html.erb just contains a few lines:

<%% initial_state = controller.render_to_string(active_template_virtual_path, formats: [:json], locals: local_assigns, layout: true) %>
<script type="text/javascript">
  window.SUPERGLUE_INITIAL_PAGE_STATE=<%%= initial_state.html_safe %>;
</script>
<div id="app"></div>

Sometimes it may make sense to have that file to override, but is there a way to eliminate the need for these erb files so that the structure looks like:

app/
|-- controllers/
|-- views/
|   |-- dashboard/
|   |   |-- index.js # The React page component
|   |   |-- index.json.props # The json for the page component

That would be so much cleaner.

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.