solder 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d86c83657628b64222a9656e535f19ca6d66a4e675c3e80e7b86d4c2a0f3835
4
- data.tar.gz: 42f8a93bca5208c7c0bab9e1d5cf764f8dfa70564dd2c43952d580df8fee8f77
3
+ metadata.gz: 00c285939bae079a1349a896571e8b315752b813715081d4d29d043a25d0c27a
4
+ data.tar.gz: '092845396d182c4082c4915a12fd9a9087666557c95cca1cae4a6be1e9078420'
5
5
  SHA512:
6
- metadata.gz: cf18a4e8990db861c48cb81585afff0e6d14d3baa98fe2769947c383af10f9aeefe5c73d0787dbf27f994920eef3aeac4910a07bf7386813bc3dde0ae1a857d9
7
- data.tar.gz: 0b08006eda6254c442df937076b51980a01b47becff672f3bca401ba73d73af641f6e1d684a325cff4396c3c58c068bbbeb491e5b109efa5335866ab83040423
6
+ metadata.gz: adb71a8e89b6c6730250dd0ba23b5ad0390822ba6d35afd130fd655a37ab6cecbcd45f1c443ece71d8e199a8fce984d6070d8dd0dcab4e6444041f0089e402bf
7
+ data.tar.gz: ea5f579f47725c9d5f148ca0f1e9dca34e2f98a81b6575595442266b36c3e264577ef96fcff94487dbc3fef0390d7c1c891c8f00b9196023ff796d02cde216dc
@@ -1,5 +1,7 @@
1
1
  module Solder
2
2
  class UiStateController < ApplicationController
3
+ include ActionView::Helpers::SanitizeHelper
4
+
3
5
  before_action :set_ui_state, only: :show
4
6
  around_action Solder.config[:around_action]
5
7
 
@@ -29,7 +31,7 @@ module Solder
29
31
  end
30
32
 
31
33
  def parsed_attributes
32
- JSON.parse(ui_state_params[:attributes])
34
+ JSON.parse(ui_state_params[:attributes]).deep_transform_values { sanitize(_1) }
33
35
  end
34
36
  end
35
37
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Copies the Solder configuration template to config/initializers
3
+
4
+ Example:
5
+ bin/rails generate solder:initializer
6
+
7
+ This will create:
8
+ config/initializers/solder.rb
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ bin/rails generate initializer Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,7 @@
1
+ class Solder::InitializerGenerator < Rails::Generators::Base
2
+ source_root File.expand_path("templates", __dir__)
3
+
4
+ def copy_controller_template
5
+ template "solder.rb", Rails.root.join("config/initializers/solder.rb")
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ class Solder::InitializerGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("templates", __dir__)
3
+ end
@@ -0,0 +1,10 @@
1
+ Solder.configure do |config|
2
+ # Specify a global around action for Solder's UIStateController
3
+ # This is useful for any assumptions your app makes, e.g. instance
4
+ # variables being present, headers being checked, or multitenancy
5
+ # (see example below)
6
+ #
7
+ # config[:around_action] = ->(_controller, action) do
8
+ # ActsAsTenant.without_tenant { action.call }
9
+ # end
10
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Copies the solder stimulus controller to app/javascript/controllers
3
+
4
+ Example:
5
+ bin/rails generate solder:stimulus
6
+
7
+ This will create:
8
+ app/javascript/controllers/solder_controller.js
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ bin/rails generate stimulus Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,7 @@
1
+ class Solder::StimulusGenerator < Rails::Generators::Base
2
+ source_root File.expand_path("templates", __dir__)
3
+
4
+ def copy_controller_template
5
+ template "solder_controller.js.tt", Rails.root.join("app/javascript/controllers/solder_controller.js")
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ class Solder::StimulusGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("templates", __dir__)
3
+ end
@@ -0,0 +1,41 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { get, patch } from '@rails/request.js'
3
+
4
+ export default class extends Controller {
5
+ static values = {
6
+ key: String
7
+ }
8
+
9
+ async connect () {
10
+ this.mutationObserver = new MutationObserver(this.mutateState.bind(this))
11
+
12
+ this.mutationObserver.observe(this.element, { attributes: true })
13
+ }
14
+
15
+ disconnect () {
16
+ this.mutationObserver?.disconnect()
17
+ }
18
+
19
+ mutateState (mutationList, observer) {
20
+ mutationList
21
+ .filter(mutation => mutation.attributeName !== 'data-solder-touch')
22
+ .forEach(async mutation => {
23
+ const body = new FormData()
24
+
25
+ const attributes = {}
26
+ this.element
27
+ .getAttributeNames()
28
+ .filter(name => name !== 'data-controller')
29
+ .map(name => {
30
+ attributes[name] = this.element.getAttribute(name)
31
+ })
32
+
33
+ body.append('key', this.keyValue)
34
+ body.append('attributes', JSON.stringify(attributes))
35
+
36
+ await patch('/solder/ui_state/update', {
37
+ body
38
+ })
39
+ })
40
+ }
41
+ }
@@ -0,0 +1,36 @@
1
+ # install @rails/request.js
2
+ if Rails.root.join("config/importmap.rb").exist?
3
+ say "Pin @rails/request.js"
4
+ append_to_file "config/importmap.rb", %(pin "@rails/request.js", preload: true\n)
5
+ else
6
+ say "Install @rails/request.js"
7
+ run "yarn add @rails/request.js"
8
+ end
9
+
10
+ gemfile = Rails.root.join("Gemfile").read
11
+
12
+ # install stimulus-rails, if not already present
13
+ if !gemfile.include? "stimulus-rails"
14
+ gem "stimulus-rails"
15
+ rails_command "stimulus:install"
16
+ say "✅ stimulus-rails has been installed"
17
+ else
18
+ say "⏩ stimulus-rails is already installed. Skipping."
19
+ end
20
+
21
+ # copy stimulus controller template
22
+ generate "solder:stimulus"
23
+
24
+ # turn on development caching
25
+ if Rails.root.join("tmp", "caching-dev.txt").exist?
26
+ say "⏩ Already caching in development. Skipping."
27
+ else
28
+ system "rails dev:cache"
29
+ say "✅ Enabled caching in development"
30
+ end
31
+
32
+ # mount engine
33
+ route 'mount Solder::Engine, at: "/solder"'
34
+
35
+ # copy initializer template
36
+ generate "solder:initializer" if yes?("Do you want to install the solder initializer template?")
@@ -0,0 +1,17 @@
1
+ if Rails.root.join("config/importmap.rb").exist?
2
+ say "Pin @rails/request.js"
3
+ append_to_file "config/importmap.rb", %(pin "@rails/request.js", preload: true\n)
4
+ else
5
+ say "Install @rails/request.js"
6
+ run "yarn add @rails/request.js"
7
+ end
8
+
9
+ # if stimulus isn't present
10
+ # bundle add stimulus-rails
11
+ # bin/rails stimulus:install
12
+ gemfile = Rails.root.join("Gemfile").read
13
+ binding.irb
14
+
15
+ say "Installing Stimulus"
16
+ gem "stimulus-rails"
17
+ rails_command "stimulus:install"
@@ -0,0 +1,41 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { get, patch } from '@rails/request.js'
3
+
4
+ export default class extends Controller {
5
+ static values = {
6
+ key: String
7
+ }
8
+
9
+ async connect () {
10
+ this.mutationObserver = new MutationObserver(this.mutateState.bind(this))
11
+
12
+ this.mutationObserver.observe(this.element, { attributes: true })
13
+ }
14
+
15
+ disconnect () {
16
+ this.mutationObserver?.disconnect()
17
+ }
18
+
19
+ mutateState (mutationList, observer) {
20
+ mutationList
21
+ .filter(mutation => mutation.attributeName !== 'data-solder-touch')
22
+ .forEach(async mutation => {
23
+ const body = new FormData()
24
+
25
+ const attributes = {}
26
+ this.element
27
+ .getAttributeNames()
28
+ .filter(name => name !== 'data-controller')
29
+ .map(name => {
30
+ attributes[name] = this.element.getAttribute(name)
31
+ })
32
+
33
+ body.append('key', this.keyValue)
34
+ body.append('attributes', JSON.stringify(attributes))
35
+
36
+ await patch('/solder/ui_state/update', {
37
+ body
38
+ })
39
+ })
40
+ }
41
+ }
data/lib/solder/engine.rb CHANGED
@@ -1,6 +1,12 @@
1
1
  module Solder
2
- def self.config
3
- Rails.application.config.solder
2
+ class << self
3
+ def config
4
+ Rails.application.config.solder
5
+ end
6
+
7
+ def configure
8
+ yield config
9
+ end
4
10
  end
5
11
 
6
12
  class Engine < ::Rails::Engine
@@ -10,6 +16,9 @@ module Solder
10
16
  config.solder[:around_action] = ->(_controller, action) { action.call }
11
17
 
12
18
  initializer "solder.check_caching" do |app|
19
+ next if called_by_installer?
20
+ next if called_by_generator?
21
+
13
22
  unless app.config.action_controller.perform_caching && app.config.cache_store != :null_store
14
23
  puts <<~WARN
15
24
  🧑‍🏭 Solder uses the Rails cache store to provide UI state persistence. Therefore, please make sure caching is enabled in your environment.
@@ -28,5 +37,17 @@ module Solder
28
37
  helper Solder::Engine.helpers
29
38
  end
30
39
  end
40
+
41
+ private
42
+
43
+ def called_by_installer?
44
+ Rake.application.top_level_tasks.include? "app:template"
45
+ rescue
46
+ false
47
+ end
48
+
49
+ def called_by_generator?
50
+ ARGV.any? { _1.include? "solder:" }
51
+ end
31
52
  end
32
53
  end
@@ -22,8 +22,11 @@ module Solder
22
22
  end
23
23
  end
24
24
 
25
- config.after_initialize do
26
- ::ApplicationHelper.include helpers
25
+ initializer "solder.helpers" do
26
+ ActiveSupport.on_load(:action_controller_base) do
27
+ include Solder::ApplicationHelper
28
+ helper Solder::Engine.helpers
29
+ end
27
30
  end
28
31
  end
29
32
  end
@@ -1,3 +1,3 @@
1
1
  module Solder
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,3 +1,3 @@
1
1
  module Solder
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,4 +1,6 @@
1
- # desc "Explaining what the task does"
2
- # task :solder do
3
- # # Task goes here
4
- # end
1
+ namespace :solder do
2
+ desc "Install Solder into the app"
3
+ task :install do
4
+ system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/install.rb", __dir__)}"
5
+ end
6
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian Rubisch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-16 00:00:00.000000000 Z
11
+ date: 2023-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 7.0.4
41
- - !ruby/object:Gem::Dependency
42
- name: stimulus-rails
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '1.1'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '1.1'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: mocha
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -121,15 +107,26 @@ files:
121
107
  - app/assets/stylesheets/solder/application.css
122
108
  - app/controllers/solder/application_controller.rb
123
109
  - app/controllers/solder/ui_state_controller.rb
124
- - app/controllers/solder/ui_state_controller.rb~
125
110
  - app/helpers/solder/application_helper.rb
126
- - app/helpers/solder/application_helper.rb~
127
111
  - app/jobs/solder/application_job.rb
128
112
  - app/mailers/solder/application_mailer.rb
129
113
  - app/models/solder/application_record.rb
130
114
  - app/views/layouts/solder/application.html.erb
131
115
  - config/routes.rb
132
- - config/routes.rb~
116
+ - lib/generators/solder/initializer/USAGE
117
+ - lib/generators/solder/initializer/USAGE~
118
+ - lib/generators/solder/initializer/initializer_generator.rb
119
+ - lib/generators/solder/initializer/initializer_generator.rb~
120
+ - lib/generators/solder/initializer/templates/solder.rb
121
+ - lib/generators/solder/initializer/templates/solder.rb~
122
+ - lib/generators/solder/stimulus/USAGE
123
+ - lib/generators/solder/stimulus/USAGE~
124
+ - lib/generators/solder/stimulus/stimulus_generator.rb
125
+ - lib/generators/solder/stimulus/stimulus_generator.rb~
126
+ - lib/generators/solder/stimulus/templates/solder_controller.js.tt
127
+ - lib/install/install.rb
128
+ - lib/install/install.rb~
129
+ - lib/install/templates/solder_controller.js.tt
133
130
  - lib/solder.rb
134
131
  - lib/solder/engine.rb
135
132
  - lib/solder/engine.rb~
@@ -156,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
153
  - !ruby/object:Gem::Version
157
154
  version: '0'
158
155
  requirements: []
159
- rubygems_version: 3.3.26
156
+ rubygems_version: 3.3.22
160
157
  signing_key:
161
158
  specification_version: 4
162
159
  summary: Simplistic UI State Management for Rails Apps using Hotwire and Caching
@@ -1,25 +0,0 @@
1
- module Solder
2
- class UiStateController < ApplicationController
3
- before_action :set_ui_state
4
-
5
- def show
6
- render json: @ui_state.value
7
- end
8
-
9
- def update
10
- @ui_state.value = JSON.parse(params[:attributes])
11
-
12
- head :ok
13
- end
14
-
15
- private
16
-
17
- def ui_state_params
18
- params.permit(:attributes, :key)
19
- end
20
-
21
- def set_ui_state
22
- @ui_state = Kredis.json params[:key]
23
- end
24
- end
25
- end
@@ -1,37 +0,0 @@
1
- module Solder
2
- module ApplicationHelper
3
- def solder_onto(name, touch: [], attribute_safelist: ["class"], &block)
4
- soldered_html = capture(&block).to_s.strip
5
- fragment = Nokogiri::HTML.fragment(soldered_html)
6
-
7
- first_fragment_child = fragment.first_element_child
8
-
9
- # rehydrate
10
- ui_state = Rails.cache.read "solder/#{solder_key(name)}"
11
-
12
- ui_state&.select { attribute_safelist.include?(_1) }&.each do |attribute_name, value|
13
- first_fragment_child[attribute_name] = sanitize value
14
- end
15
-
16
- # add stimulus controller and create unique key
17
- first_fragment_child["data-controller"] = "#{first_fragment_child["data-controller"]} solder".strip
18
- first_fragment_child["data-solder-key-value"] = solder_key(name)
19
-
20
- first_fragment_child["data-solder-touch"] ||= Array(touch).map(&:to_sgid).map(&:to_s).join(":")
21
-
22
- first_fragment_child.to_html.html_safe
23
- end
24
-
25
- def solder_key(name)
26
- key = cache_fragment_name(name, skip_digest: true)
27
- .flatten
28
- .compact
29
- .map(&:cache_key)
30
- .join(":")
31
-
32
- key += ":#{caller.find { _1 =~ /html/ }}"
33
-
34
- ActiveSupport::Digest.hexdigest(key)
35
- end
36
- end
37
- end
data/config/routes.rb~ DELETED
@@ -1,4 +0,0 @@
1
- Solder::Engine.routes.draw do
2
- get 'ui_state/update'
3
- get 'ui_state/show'
4
- end