Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .github/workflows/build_rails.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,31 @@ jobs:

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 23.10.0
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Which bundler?
run: bundle -v
- name: Which chromedriver?
run: chromedriver -v
- name: Using Gemfile
run: |
mv -f Gemfile.${{ matrix.version }} ./Gemfile
- name: npm install on dummy app
run: cd test/dummy && npm install && npm run build
- name: Bundle install
run: bundle install
- name: Run unit test
run: bundle exec rake test
- name: Run acceptance test
run: BUNDLE_GEMFILE='' ruby -Ilib:test test/acceptance/superglue_installation_acceptance.rb
- name: Upload artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: superglue_rails_run${{ matrix.ruby }}${{ matrix.version }}
path: test/dummy/tmp/screenshots/
retention-days: 7
2 changes: 2 additions & 0 deletions Gemfile.70
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ gem 'minitest'
gem 'rake'
gem 'sqlite3', '~> 1.4'
gem 'git'
gem 'cuprite'
gem 'puma'
2 changes: 2 additions & 0 deletions Gemfile.71
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ gem 'minitest'
gem 'rake'
gem 'sqlite3', '~> 1.4'
gem 'git'
gem 'cuprite'
gem 'puma'
3 changes: 3 additions & 0 deletions Gemfile.72
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ gem 'minitest'
gem 'rake'
gem 'sqlite3', '~> 1.4'
gem 'git'
gem 'cuprite'
gem 'puma'
gem 'propshaft'
3 changes: 3 additions & 0 deletions Gemfile.80
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ gem 'minitest'
gem 'rake'
gem 'sqlite3'
gem 'git'
gem 'cuprite'
gem 'puma'
gem 'propshaft'
3 changes: 3 additions & 0 deletions Gemfile.main
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ gem 'capybara'
gem 'minitest'
gem 'rake'
gem 'sqlite3', '~> 1.4'
gem 'git'
gem 'cuprite'
gem 'puma'
20 changes: 19 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
require "rake/testtask"
require "standard/rake"

Rake::TestTask.new do |t|
task :build_dummy_js do
package_path = File.join(__FILE__, "test/dummy/package.json")
puts package_path
superglue_version = ENV["SUPERGLUEJS_PATH"] || "^2.0.0-alpha.1"

if File.exist?(package_path)
package = JSON.parse(File.read(package_path))
if package.dig("dependencies", "@thoughtbot/superglue") != superglue_version
package["dependencies"] ||= {}
package["dependencies"]["@thoughtbot/superglue"] = superglue_version
File.write(package_path, JSON.pretty_generate(package))
end
end

puts "Installing and building JS dependencies..."
system("cd test/dummy && npm install && npm run build") or abort("npm install/build failed")
end

Rake::TestTask.new(test: :build_dummy_js) do |t|
t.libs << "test"
t.pattern = "test/**/*_test.rb"
t.warning = false
Expand Down
8 changes: 6 additions & 2 deletions app/helpers/superglue/streams_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def broadcast_append_props(model: nil, fragment: nil, save_as: nil, options: {},
end

def broadcast_save_props(model: nil, partial: nil, fragment: nil, options: {}, **rendering)
if model && !fragment
fragment = fragment_id(model)
end

broadcast_action_props(action: "save", model:, fragment:, options:, **rendering)
end

Expand All @@ -56,8 +60,8 @@ def broadcast_action_props(action:, partial: nil, model: nil, fragment: nil, opt
json = instance_variable_get(:@__json)

json.child! do
json.fragmentKeys [fragment]
json.action action
json.fragmentIds [fragment]
json.handler action
json.options(options)
json.data(partial: [partial, rendering]) do
end
Expand Down
4 changes: 2 additions & 2 deletions app/models/concerns/superglue/broadcastable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ def broadcast_action_later(action:, fragment: broadcast_fragment_default, option
broadcast_action_later_to self, action: action, fragment: fragment, options: options, **rendering
end

private

def broadcast_fragment_default
self.class.broadcast_fragment_default
end

private

def extract_options_and_add_fragment(rendering = {}, fragment: broadcast_fragment_default)
broadcast_rendering_with_defaults(rendering).tap do |options|
Expand Down
6 changes: 3 additions & 3 deletions app/views/superglue/layouts/_stream_message.json.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ else
end

json.fragments json.fragments!
json.type "message"
json.action broadcast_action
json.fragmentKeys broadcast_fragment_keys
json.action "handleStreamMessage"
json.handler broadcast_action
json.fragmentIds broadcast_fragment_keys
json.options broadcast_options
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ json.data do
end

json.fragments json.fragments!
json.type "fragment"
json.action "handleStreamResponse"

json.flash flash.to_h
2 changes: 1 addition & 1 deletion lib/generators/superglue/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def create_files
insert_jsx_rendering_defaults

say "Installing Superglue and friends"
run "yarn add react react-dom @reduxjs/toolkit react-redux @thoughtbot/superglue"
run "yarn add react react-dom @reduxjs/toolkit react-redux @thoughtbot/superglue@2.0.0-alpha.1"

if use_typescript
run "yarn add -D @types/react-dom @types/react @types/node @thoughtbot/candy_wrapper"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ if protect_against_forgery?
json.csrfToken form_authenticity_token
end

json.action "stream"
json.action "handleStreamResponse"
2 changes: 1 addition & 1 deletion superglue.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ Gem::Specification.new do |s|
s.files = Dir["MIT-LICENSE", "README.md", "lib/**/*", "app/**/*"]

s.add_dependency "actionpack", ">= 7.0", "< 9.0"
s.add_dependency "props_template", "~> 0.37"
s.add_dependency "props_template", "~> 1.0.0.alpha"
s.add_dependency "form_props", "~> 0.2"
end
23 changes: 11 additions & 12 deletions test/acceptance/superglue_installation_acceptance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,23 @@ def update_package_json
end

def install_superglue
Dir.chdir(ROOT_DIR) do
successfully "rm -rf ./superglue"
Git.clone("https://github.com/thoughtbot/superglue.git")
end

Dir.chdir(SUPERGLUE_SUPERGLUE_PATH) do
successfully "npm install"
successfully "npm run build"
successfully "npm pack"
end
successfully "echo \"gem 'props_template'\" >> Gemfile"
# Dir.chdir(ROOT_DIR) do
# successfully "rm -rf ./superglue"
# Git.clone("https://github.com/thoughtbot/superglue.git")
# end

# Dir.chdir(SUPERGLUE_SUPERGLUE_PATH) do
# successfully "npm install"
# successfully "npm run build"
# successfully "npm pack"
# end
successfully "echo \"gem 'superglue', path: '#{SUPERGLUE_RAILS_PATH}'\" >> Gemfile"
successfully "bundle install"

FileUtils.rm_f("app/javascript/application.js")

successfully "bundle exec rails generate superglue:install"
update_package_json
# update_package_json
successfully "yarn install --cache-folder /tmp/.junk; rm -rf /tmp/.junk"
end

Expand Down
2 changes: 1 addition & 1 deletion test/dummy/app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ApplicationController < ActionController::Base
# superglue_template "application/superglue"

# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
allow_browser versions: :modern
# allow_browser versions: :modern

superglue_template "application/superglue"
end
11 changes: 9 additions & 2 deletions test/dummy/app/controllers/messages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ def section
end

def create
@message = Message.new(id: 1, content: "My message")

respond_to do |format|
format.html { redirect_to message_url(id: 1) }
format.turbo_stream { render turbo_stream: turbo_stream.append(:messages, "message_1"), status: :created }
format.json { render layout: "stream" }
end
end

def update
@message = Message.new(id: 1, content: "My message")
@message = Message.new(id: params[:id], content: "Updated message")

respond_to do |format|
format.html { redirect_to message_url(id: params[:id]) }
format.json { render layout: "stream" }
end
end
end
2 changes: 2 additions & 0 deletions test/dummy/app/javascript/page_to_page_mapping.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import MessagesIndex from "@views/messages";
import MessagesShow from "@views/messages/show";
import ProfileIndex from "@views/users/profiles";
import SectionIndex from "@views/messages/section"
// import your page component
Expand Down Expand Up @@ -33,6 +34,7 @@ import SectionIndex from "@views/messages/section"
//
const pageIdentifierToPageComponent = {
'messages/index': MessagesIndex,
'messages/show': MessagesShow,
'users/profiles/index': ProfileIndex,
'messages/section': SectionIndex,
};
Expand Down
12 changes: 6 additions & 6 deletions test/dummy/app/views/layouts/stream.json.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ json.data do
yield json
end
end
# json.fragments json.fragments!
# json.assets [ asset_path('application.js') ]
json.fragments json.fragments!
json.assets [ asset_path('application.js') ]

# if protect_against_forgery?
# json.csrfToken form_authenticity_token
# end
if protect_against_forgery?
json.csrfToken form_authenticity_token
end

# json.action "stream"
json.action "handleStreamResponse"
1 change: 1 addition & 0 deletions test/dummy/app/views/messages/create.json.props
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
broadcast_save_props(model: @message)
2 changes: 1 addition & 1 deletion test/dummy/app/views/messages/index.json.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
json.header "Messages"

json.streamFromMessages stream_from_props("messages")
json.messages(partial: "message_list", fragment: "messages") do
json.messages(partial: ["message_list", fragment: "messages"]) do
end
13 changes: 7 additions & 6 deletions test/dummy/app/views/messages/index.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import React from 'react'
import { useContent, useFragment, useStreamSource } from '@thoughtbot/superglue'
import { useContent, unproxy, useStreamSource } from '@thoughtbot/superglue'

const isFragment = (data) => "__id" in data

const Message = ({body}) => <p>{body}</p>

const MessageFragment = ({fragment}) => {
const [{body}] = useFragment(fragment)
const {body} = useContent(fragment)
return <p>{body}</p>
}

export default function MessagesIndex() {
const content = useContent()
const {
header,
streamFromMessages,
messages: messagesFragment
} = useContent()
const [spotlight] = useFragment('message-1')
const [messages] = useFragment(messagesFragment)
} = content

const spotlight = useContent('message-1', {optional: true})
useStreamSource(streamFromMessages)
const messages = unproxy(content.messages)

return (
<div>
Expand Down
2 changes: 1 addition & 1 deletion test/dummy/app/views/messages/section.json.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
json.header "Messages in a section"

json.streamFromMessages stream_from_props("messages", channel: SectionChannel, section: "important")
json.messages(partial: "message_list_section", fragment: "messages") do
json.messages(partial: ["message_list_section", fragment: "messages"]) do
end
9 changes: 4 additions & 5 deletions test/dummy/app/views/messages/section.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import React from 'react'
import { useContent, useFragment, useStreamSource } from '@thoughtbot/superglue'
import { useContent, useStreamSource, unproxy } from '@thoughtbot/superglue'

const Message = ({body}) => <p>{body}</p>

export default function SectionIndex() {
const content = useContent()
const {
header,
streamFromMessages,
messages: messagesFragment
} = useContent()
const [messages] = useFragment(messagesFragment)
} = content
useStreamSource(streamFromMessages)

return (
<div>
<h1>{header}</h1>
<div id="messages">
{ messages.map((msg) => <Message {...msg}/>) }
{ content.messages.map((msg) => <Message {...msg}/>) }
</div>
</div>
)
Expand Down
12 changes: 12 additions & 0 deletions test/dummy/app/views/messages/show.json.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
json.message(partial: [
"message",
fragment: fragment_id(@message),
locals: {message: @message}
]) do
end

json.updateMessageForm do
form_props(model: @message) do |f|
f.submit
end
end
22 changes: 22 additions & 0 deletions test/dummy/app/views/messages/show.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react'
import { useContent } from '@thoughtbot/superglue'
import { SubmitButton, Form } from '@javascript/components'

export default function MessagesShow() {
const content = useContent()
const {form, extras, inputs} = content.updateMessageForm


return (
<div>
<h1> Message Show</h1>
<div>
{content.message.body}
</div>

<Form {...form} extras={extras} data-sg-visit={true}>
<SubmitButton {...inputs.submit}/>
</Form>
</div>
)
}
1 change: 1 addition & 0 deletions test/dummy/app/views/messages/update.json.props
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
broadcast_save_props(model: @message)
Loading
Loading