tailmix 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +76 -145
- data/app/javascript/tailmix/runtime/action_dispatcher.js +132 -0
- data/app/javascript/tailmix/runtime/component.js +130 -0
- data/app/javascript/tailmix/runtime/index.js +130 -0
- data/app/javascript/tailmix/runtime/plugins.js +35 -0
- data/app/javascript/tailmix/runtime/updater.js +140 -0
- data/docs/01_getting_started.md +39 -0
- data/examples/_modal_component.arb +17 -25
- data/examples/button.rb +81 -0
- data/examples/helpers.rb +25 -0
- data/examples/modal_component.rb +32 -164
- data/lib/tailmix/definition/builders/action_builder.rb +39 -0
- data/lib/tailmix/definition/{contexts → builders}/actions/element_builder.rb +1 -1
- data/lib/tailmix/definition/{contexts → builders}/attribute_builder.rb +1 -1
- data/lib/tailmix/definition/builders/component_builder.rb +81 -0
- data/lib/tailmix/definition/{contexts → builders}/dimension_builder.rb +2 -2
- data/lib/tailmix/definition/builders/element_builder.rb +83 -0
- data/lib/tailmix/definition/builders/reactor_builder.rb +43 -0
- data/lib/tailmix/definition/builders/rule_builder.rb +58 -0
- data/lib/tailmix/definition/builders/state_builder.rb +21 -0
- data/lib/tailmix/definition/{contexts → builders}/variant_builder.rb +17 -2
- data/lib/tailmix/definition/context_builder.rb +19 -13
- data/lib/tailmix/definition/merger.rb +2 -1
- data/lib/tailmix/definition/payload_proxy.rb +16 -0
- data/lib/tailmix/definition/result.rb +17 -18
- data/lib/tailmix/dev/docs.rb +32 -7
- data/lib/tailmix/dev/tools.rb +0 -5
- data/lib/tailmix/dsl.rb +3 -5
- data/lib/tailmix/engine.rb +11 -1
- data/lib/tailmix/html/attributes.rb +22 -12
- data/lib/tailmix/middleware/registry_cleaner.rb +17 -0
- data/lib/tailmix/registry.rb +37 -0
- data/lib/tailmix/runtime/action_proxy.rb +29 -0
- data/lib/tailmix/runtime/attribute_builder.rb +102 -0
- data/lib/tailmix/runtime/attribute_cache.rb +23 -0
- data/lib/tailmix/runtime/context.rb +51 -47
- data/lib/tailmix/runtime/facade_builder.rb +8 -5
- data/lib/tailmix/runtime/state.rb +36 -0
- data/lib/tailmix/runtime/state_proxy.rb +34 -0
- data/lib/tailmix/runtime.rb +0 -1
- data/lib/tailmix/version.rb +1 -1
- data/lib/tailmix/view_helpers.rb +49 -0
- data/lib/tailmix.rb +4 -2
- metadata +34 -21
- data/app/javascript/tailmix/finder.js +0 -15
- data/app/javascript/tailmix/index.js +0 -7
- data/app/javascript/tailmix/mutator.js +0 -28
- data/app/javascript/tailmix/runner.js +0 -7
- data/app/javascript/tailmix/stimulus_adapter.js +0 -37
- data/lib/tailmix/definition/contexts/action_builder.rb +0 -31
- data/lib/tailmix/definition/contexts/element_builder.rb +0 -41
- data/lib/tailmix/definition/contexts/stimulus_builder.rb +0 -101
- data/lib/tailmix/dev/stimulus_generator.rb +0 -124
- data/lib/tailmix/runtime/stimulus/compiler.rb +0 -59
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Tailmix
|
|
4
|
-
module Definition
|
|
5
|
-
module Contexts
|
|
6
|
-
class StimulusBuilder
|
|
7
|
-
attr_reader :definitions
|
|
8
|
-
|
|
9
|
-
def initialize
|
|
10
|
-
@definitions = []
|
|
11
|
-
@current_context = nil
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def controller(name)
|
|
15
|
-
@definitions << { type: :controller, name: name }
|
|
16
|
-
context(name)
|
|
17
|
-
end
|
|
18
|
-
alias_method :ctr, :controller
|
|
19
|
-
|
|
20
|
-
def context(name)
|
|
21
|
-
@current_context = name.to_s
|
|
22
|
-
self
|
|
23
|
-
end
|
|
24
|
-
alias_method :ctx, :context
|
|
25
|
-
|
|
26
|
-
def target(target_name)
|
|
27
|
-
raise "A controller context must be set..." unless @current_context
|
|
28
|
-
@definitions << { type: :target, controller: @current_context, name: target_name }
|
|
29
|
-
self
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def action(*args)
|
|
33
|
-
ensure_context!
|
|
34
|
-
|
|
35
|
-
action_data = case args.first
|
|
36
|
-
when String
|
|
37
|
-
{ type: :raw, content: args.first }
|
|
38
|
-
when Hash
|
|
39
|
-
{ type: :hash, content: args.first }
|
|
40
|
-
else
|
|
41
|
-
{ type: :tuple, content: args }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
@definitions << { type: :action, controller: @current_context, data: action_data }
|
|
45
|
-
self
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def value(value_name, value: nil, call: nil, method: nil)
|
|
49
|
-
ensure_context!
|
|
50
|
-
|
|
51
|
-
source = if !value.nil?
|
|
52
|
-
{ type: :literal, content: value }
|
|
53
|
-
elsif call.is_a?(Proc)
|
|
54
|
-
{ type: :proc, content: call }
|
|
55
|
-
elsif method.is_a?(Symbol) || method.is_a?(String)
|
|
56
|
-
{ type: :method, content: method.to_sym }
|
|
57
|
-
else
|
|
58
|
-
raise ArgumentError, "You must provide one of value:, call:, or method: keyword arguments."
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
@definitions << {
|
|
62
|
-
type: :value,
|
|
63
|
-
controller: @current_context,
|
|
64
|
-
name: value_name,
|
|
65
|
-
source: source
|
|
66
|
-
}
|
|
67
|
-
self
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def action_payload(action_name, as: nil)
|
|
71
|
-
ensure_context!
|
|
72
|
-
value_name = as || "#{action_name}_action"
|
|
73
|
-
|
|
74
|
-
@definitions << {
|
|
75
|
-
type: :action_payload,
|
|
76
|
-
controller: @current_context,
|
|
77
|
-
action_name: action_name.to_sym,
|
|
78
|
-
value_name: value_name.to_sym
|
|
79
|
-
}
|
|
80
|
-
self
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def param(params_hash)
|
|
84
|
-
ensure_context!
|
|
85
|
-
@definitions << { type: :param, controller: @current_context, params: params_hash }
|
|
86
|
-
self
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def build_definition
|
|
90
|
-
Definition::Result::Stimulus.new(definitions: @definitions.freeze)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
private
|
|
94
|
-
|
|
95
|
-
def ensure_context!
|
|
96
|
-
raise "A controller context must be set via .controller() or .context() before this call." unless @current_context
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Tailmix
|
|
4
|
-
module Dev
|
|
5
|
-
class StimulusGenerator
|
|
6
|
-
def initialize(definition, component_name)
|
|
7
|
-
@definition = definition
|
|
8
|
-
@component_name = component_name
|
|
9
|
-
@stimulus_defs = definition.elements.values.flat_map(&:stimulus).flat_map(&:definitions)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def scaffold(controller_name = nil, show_docs: false)
|
|
13
|
-
controllers_to_generate = controller_name ? [ controller_name.to_s ] : all_controllers
|
|
14
|
-
|
|
15
|
-
output = controllers_to_generate.map do |name|
|
|
16
|
-
defs = @stimulus_defs.select { |d| d[:controller] == name }
|
|
17
|
-
|
|
18
|
-
show_docs ? generate_docs_for(name, defs) + "\n" : generate_js_for(name, defs) + ("-" * 60) + "\n"
|
|
19
|
-
end
|
|
20
|
-
output.join("\n")
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def generate_docs_for(controller_name, defs)
|
|
26
|
-
output = [ "Stimulus:" ]
|
|
27
|
-
output << " - on `#{controller_name}` controller:"
|
|
28
|
-
targets = defs.select { |d| d[:type] == :target }.map { |d| d[:name] }
|
|
29
|
-
output << " - Targets: #{targets.join(', ')}" if targets.any?
|
|
30
|
-
|
|
31
|
-
actions = action_methods(defs)
|
|
32
|
-
output << " - Actions: #{actions.join(', ')}" if actions.any?
|
|
33
|
-
|
|
34
|
-
values = defs.select { |d| d[:type] == :value }.map { |d| d[:name] }
|
|
35
|
-
output << " - Values: #{values.join(', ')}" if values.any?
|
|
36
|
-
|
|
37
|
-
output.join("\n")
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def generate_js_for(controller_name, defs)
|
|
41
|
-
targets = defs.select { |d| d[:type] == :target }.map { |d| "'#{d[:name]}'" }.uniq.join(", ")
|
|
42
|
-
|
|
43
|
-
payload_actions = defs.select { |d| d[:type] == :action_payload }
|
|
44
|
-
simple_values = defs.select { |d| d[:type] == :value }
|
|
45
|
-
|
|
46
|
-
value_names = (payload_actions.map { |d| d[:value_name] } + simple_values.map { |d| d[:name] })
|
|
47
|
-
.uniq.map { |name| "#{snake_to_camel(name.to_s)}: Object" }.join(", ")
|
|
48
|
-
|
|
49
|
-
isomorphic_methods = payload_actions.map do |payload_def|
|
|
50
|
-
action_name_camel = snake_to_camel(payload_def[:action_name].to_s)
|
|
51
|
-
value_name_camel = snake_to_camel(payload_def[:value_name].to_s)
|
|
52
|
-
|
|
53
|
-
" #{action_name_camel}(event) {\n if (event) event.preventDefault();\n Tailmix.run({ config: this.#{value_name_camel}Value, controller: this });\n }"
|
|
54
|
-
end.join.strip
|
|
55
|
-
|
|
56
|
-
standard_action_names = defs.select { |d| d[:type] == :action }
|
|
57
|
-
.flat_map { |d| extract_action_methods(d[:data]) }
|
|
58
|
-
.uniq
|
|
59
|
-
|
|
60
|
-
implemented_action_names = payload_actions.map { |d| d[:action_name].to_s }
|
|
61
|
-
stub_methods = (standard_action_names - implemented_action_names).map do |method_name|
|
|
62
|
-
method_name_camel = snake_to_camel(method_name)
|
|
63
|
-
" #{method_name_camel}() {\n console.log('#{controller_name}##{method_name_camel} fired');\n }"
|
|
64
|
-
end.join("\n\n")
|
|
65
|
-
|
|
66
|
-
js_methods = [isomorphic_methods, stub_methods].reject(&:empty?).join("\n\n")
|
|
67
|
-
|
|
68
|
-
<<~JAVASCRIPT
|
|
69
|
-
// Generated by Tailmix for the "#{controller_name}" controller
|
|
70
|
-
// Path: app/javascript/controllers/#{controller_name.tr('_', '-')}_controller.js
|
|
71
|
-
import { Controller } from "@hotwired/stimulus"
|
|
72
|
-
import Tailmix from "tailmix"
|
|
73
|
-
|
|
74
|
-
export default class extends Controller {
|
|
75
|
-
static targets = [#{targets}]
|
|
76
|
-
static values = { #{value_names} }
|
|
77
|
-
|
|
78
|
-
connect() {
|
|
79
|
-
console.log("#{controller_name} controller connected to", this.element);
|
|
80
|
-
}
|
|
81
|
-
#{js_methods}
|
|
82
|
-
}
|
|
83
|
-
JAVASCRIPT
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def extract_action_methods(action_data)
|
|
87
|
-
case action_data[:type]
|
|
88
|
-
when :raw
|
|
89
|
-
action_data[:content].to_s.scan(/#(\w+)/).flatten
|
|
90
|
-
when :hash
|
|
91
|
-
action_data[:content].values.map(&:to_s)
|
|
92
|
-
when :tuple
|
|
93
|
-
[action_data[:content][1].to_s]
|
|
94
|
-
else
|
|
95
|
-
[]
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def all_controllers
|
|
100
|
-
@stimulus_defs.map { |d| d[:controller] }.compact.uniq
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def action_methods(defs)
|
|
104
|
-
defs.select { |d| d[:type] == :action }.flat_map do |action_definition|
|
|
105
|
-
data = action_definition[:data]
|
|
106
|
-
case data[:type]
|
|
107
|
-
when :raw
|
|
108
|
-
data[:content].to_s.scan(/#(\w+)/).flatten
|
|
109
|
-
when :hash
|
|
110
|
-
data[:content].values.map(&:to_s)
|
|
111
|
-
when :tuple
|
|
112
|
-
[ data[:content][1].to_s ]
|
|
113
|
-
else
|
|
114
|
-
[]
|
|
115
|
-
end
|
|
116
|
-
end.uniq
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def snake_to_camel(str)
|
|
120
|
-
str.split("_").map.with_index { |word, i| i.zero? ? word : word.capitalize }.join
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Tailmix
|
|
4
|
-
module Runtime
|
|
5
|
-
module Stimulus
|
|
6
|
-
class Compiler
|
|
7
|
-
|
|
8
|
-
def self.call(definition:, data_map:, root_definition:, component:)
|
|
9
|
-
(definition.definitions || []).each do |rule|
|
|
10
|
-
builder = data_map.stimulus
|
|
11
|
-
|
|
12
|
-
case rule[:type]
|
|
13
|
-
when :controller
|
|
14
|
-
builder.controller(rule[:name])
|
|
15
|
-
when :action
|
|
16
|
-
action_data = rule[:data]
|
|
17
|
-
controller_name = rule[:controller]
|
|
18
|
-
|
|
19
|
-
action_string = case action_data[:type]
|
|
20
|
-
when :raw
|
|
21
|
-
action_data[:content]
|
|
22
|
-
when :hash
|
|
23
|
-
action_data[:content].map { |event, method| "#{event}->#{controller_name}##{method}" }.join(" ")
|
|
24
|
-
when :tuple
|
|
25
|
-
event, method = action_data[:content]
|
|
26
|
-
"#{event}->#{controller_name}##{method}"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
builder.context(controller_name).action(action_string)
|
|
30
|
-
when :target
|
|
31
|
-
builder.context(rule[:controller]).target(rule[:name])
|
|
32
|
-
when :value
|
|
33
|
-
source = rule[:source]
|
|
34
|
-
|
|
35
|
-
resolved_value = case source[:type]
|
|
36
|
-
when :literal
|
|
37
|
-
source[:content]
|
|
38
|
-
when :proc
|
|
39
|
-
source[:content].call
|
|
40
|
-
when :method
|
|
41
|
-
component.public_send(source[:content])
|
|
42
|
-
else
|
|
43
|
-
# type code here
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
builder.context(rule[:controller]).value(rule[:name], resolved_value)
|
|
47
|
-
|
|
48
|
-
when :param
|
|
49
|
-
builder.context(rule[:controller]).param(rule[:params])
|
|
50
|
-
when :action_payload
|
|
51
|
-
action = root_definition.actions.fetch(rule[:action_name])
|
|
52
|
-
builder.context(rule[:controller]).value(rule[:value_name], action.to_h)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|