joys 0.1.0 → 0.1.2
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/.DS_Store +0 -0
- data/CHANGELOG.md +9 -0
- data/README.md +1012 -3
- data/joys-0.1.0.gem +0 -0
- data/joys-0.1.1.gem +0 -0
- data/lib/joys/core.rb +161 -0
- data/lib/joys/helpers.rb +111 -0
- data/lib/joys/styles.rb +156 -0
- data/lib/joys/tags.rb +120 -0
- data/lib/joys/version.rb +1 -1
- data/lib/joys.rb +4 -5
- metadata +7 -1
data/joys-0.1.0.gem
ADDED
Binary file
|
data/joys-0.1.1.gem
ADDED
Binary file
|
data/lib/joys/core.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# lib/joys/core.rb
|
3
|
+
module Joys
|
4
|
+
@css_path = "public/css";@cache={};@templates={};@compiled_styles={};@consolidated_cache={};@current_component = nil;@layouts={}
|
5
|
+
class << self
|
6
|
+
attr_reader :cache, :templates, :compiled_styles, :consolidated_cache, :layouts
|
7
|
+
attr_accessor :current_component, :current_page, :css_path
|
8
|
+
end
|
9
|
+
def self.reset!
|
10
|
+
@cache.clear
|
11
|
+
@templates.clear
|
12
|
+
@compiled_styles.clear
|
13
|
+
@consolidated_cache.clear
|
14
|
+
@layouts.clear
|
15
|
+
@current_component = nil
|
16
|
+
@current_page = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.cache(cache_id, *args, &block);cache_key = [cache_id, args.hash];@cache[cache_key] ||= block.call;end
|
20
|
+
def self.define(type, name, &template)
|
21
|
+
full_name = "#{type}_#{name}";@templates[full_name] = template
|
22
|
+
case type
|
23
|
+
when :layout
|
24
|
+
@layouts[name] = Joys::Render.layout(&template)
|
25
|
+
define_singleton_method(full_name) { @layouts[name] }
|
26
|
+
when :page
|
27
|
+
define_singleton_method(full_name) do |**locals|
|
28
|
+
old_component = @current_component
|
29
|
+
old_page = @current_page
|
30
|
+
@current_page = full_name
|
31
|
+
@current_component = nil
|
32
|
+
result = cache(full_name, locals.hash) do
|
33
|
+
renderer = Object.new
|
34
|
+
renderer.extend(Render::Helpers)
|
35
|
+
renderer.extend(Tags)
|
36
|
+
locals.each { |k, v| renderer.instance_variable_set("@#{k}", v) }
|
37
|
+
renderer.instance_eval do
|
38
|
+
@bf = String.new(capacity: 8192)
|
39
|
+
@slots={}
|
40
|
+
@used_components = Set.new
|
41
|
+
@current_page = full_name # Pass page context to renderer
|
42
|
+
end
|
43
|
+
renderer.instance_exec(&template)
|
44
|
+
renderer.instance_variable_get(:@bf)
|
45
|
+
end
|
46
|
+
@current_component = old_component
|
47
|
+
@current_page = old_page
|
48
|
+
result.freeze
|
49
|
+
end
|
50
|
+
else
|
51
|
+
define_singleton_method(full_name) do |*args|
|
52
|
+
old_component = @current_component
|
53
|
+
@current_component = full_name if type == :comp
|
54
|
+
result = cache(full_name, *args) do
|
55
|
+
Joys::Render.compile { instance_exec(*args, &template) }
|
56
|
+
end
|
57
|
+
@current_component = old_component
|
58
|
+
result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
def self.clear_cache!
|
63
|
+
@cache.clear
|
64
|
+
@compiled_styles.clear
|
65
|
+
@consolidated_cache.clear
|
66
|
+
end
|
67
|
+
def self.page(name, **locals)
|
68
|
+
page_name = "page_#{name}"
|
69
|
+
renderer = Object.new
|
70
|
+
renderer.extend(Render::Helpers)
|
71
|
+
renderer.extend(Tags)
|
72
|
+
locals.each { |k, v| renderer.instance_variable_set("@#{k}", v) }
|
73
|
+
renderer.instance_eval do
|
74
|
+
@bf = String.new(capacity: 8192)
|
75
|
+
@slots={}
|
76
|
+
@used_components = Set.new
|
77
|
+
@current_page = page_name
|
78
|
+
@used_components.add(page_name) if Joys.compiled_styles[page_name]
|
79
|
+
end
|
80
|
+
page_template = @templates[page_name]
|
81
|
+
raise "No page template defined for #{name}" unless page_template
|
82
|
+
old_page = @current_page
|
83
|
+
@current_page = page_name
|
84
|
+
renderer.instance_exec(&page_template)
|
85
|
+
@current_page = old_page
|
86
|
+
renderer.instance_variable_get(:@bf).freeze
|
87
|
+
end
|
88
|
+
def self.comp(name, *args, **locals, &block)
|
89
|
+
renderer = Object.new
|
90
|
+
renderer.extend(Render::Helpers)
|
91
|
+
renderer.extend(Tags)
|
92
|
+
locals.each { |k, v| renderer.instance_variable_set("@#{k}", v) }
|
93
|
+
renderer.instance_eval do
|
94
|
+
@bf = String.new(capacity: 8192)
|
95
|
+
@slots = {}
|
96
|
+
@used_components = Set.new
|
97
|
+
end
|
98
|
+
|
99
|
+
comp_template = @templates["comp_#{name}"]
|
100
|
+
raise "No comp template defined for #{name}" unless comp_template
|
101
|
+
|
102
|
+
old_component = @current_component
|
103
|
+
@current_component = "comp_#{name}"
|
104
|
+
|
105
|
+
# Pass the block as a regular argument to the template
|
106
|
+
renderer.instance_exec(*args, block, &comp_template)
|
107
|
+
|
108
|
+
@current_component = old_component
|
109
|
+
renderer.instance_variable_get(:@bf).freeze
|
110
|
+
end
|
111
|
+
def self.html(&block)
|
112
|
+
renderer = Object.new
|
113
|
+
renderer.extend(Render::Helpers)
|
114
|
+
renderer.extend(Tags)
|
115
|
+
renderer.instance_variable_set(:@bf, String.new)
|
116
|
+
|
117
|
+
# Only inherit page context for style compilation
|
118
|
+
renderer.instance_variable_set(:@current_page, current_page) if current_page
|
119
|
+
|
120
|
+
renderer.instance_eval(&block)
|
121
|
+
renderer.instance_variable_get(:@bf)
|
122
|
+
end
|
123
|
+
module Render
|
124
|
+
def self.compile(&block)
|
125
|
+
context = Object.new;context.extend(Helpers);context.extend(Tags)
|
126
|
+
context.instance_eval { @bf = String.new(capacity: 8192);@slots={};@used_components = Set.new}
|
127
|
+
context.instance_eval(&block)
|
128
|
+
context.instance_variable_get(:@bf).freeze
|
129
|
+
end
|
130
|
+
def self.layout(&layout_block)
|
131
|
+
template = Object.new;template.extend(Helpers);template.extend(Tags)
|
132
|
+
template.instance_eval { @bf = String.new;@slots={};@used_components = Set.new }
|
133
|
+
template.define_singleton_method(:pull) { |name = :main| @bf << "<!--SLOT:#{name}-->"; nil }
|
134
|
+
template.instance_eval(&layout_block)
|
135
|
+
precompiled = template.instance_variable_get(:@bf).freeze
|
136
|
+
->(context, &content_block) {
|
137
|
+
context.instance_eval(&content_block) if content_block
|
138
|
+
slots = context.instance_variable_get(:@slots) || {}
|
139
|
+
used_components = context.instance_variable_get(:@used_components)
|
140
|
+
if used_components && !used_components.empty?
|
141
|
+
auto_styles = Joys::Styles.render_consolidated_styles(used_components)
|
142
|
+
else
|
143
|
+
auto_styles = ""
|
144
|
+
end
|
145
|
+
slot_regex = /<!--SLOT:(.*?)-->/.freeze
|
146
|
+
result = precompiled.gsub(slot_regex) do |match|
|
147
|
+
slot_name = $1.to_sym
|
148
|
+
slots[slot_name] || ""
|
149
|
+
end
|
150
|
+
if result.include?('<!--STYLES-->')
|
151
|
+
result = result.gsub(/<!--STYLES-->/, auto_styles)
|
152
|
+
end
|
153
|
+
if result.include?('<!--EXTERNAL_STYLES-->')
|
154
|
+
external_styles = Joys::Styles.render_external_styles(used_components)
|
155
|
+
result = result.gsub(/<!--EXTERNAL_STYLES-->/, external_styles)
|
156
|
+
end
|
157
|
+
result
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/lib/joys/helpers.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# lib/joys/helpers.rb
|
3
|
+
module Joys
|
4
|
+
module Render
|
5
|
+
module Helpers
|
6
|
+
def txt(content); @bf << CGI.escapeHTML(content.to_s); nil; end
|
7
|
+
def raw(content); @bf << content.to_s; nil; end
|
8
|
+
def push(name, &block)
|
9
|
+
old_buffer = @bf;@slots ||= {}
|
10
|
+
@bf = String.new;instance_eval(&block)
|
11
|
+
content = @bf;@slots[name] = content
|
12
|
+
@bf = old_buffer;nil
|
13
|
+
end
|
14
|
+
def pull(name = :main)
|
15
|
+
@slots ||= {};@bf << @slots[name].to_s if @slots[name];nil
|
16
|
+
end
|
17
|
+
def pull_styles;@bf << "<!--STYLES-->";nil;end
|
18
|
+
def pull_external_styles;@bf << "<!--EXTERNAL_STYLES-->";nil;end
|
19
|
+
|
20
|
+
def comp(name, *args, **kwargs, &block)
|
21
|
+
comp_name = "comp_#{name}"
|
22
|
+
@used_components ||= Set.new
|
23
|
+
@used_components.add(comp_name)
|
24
|
+
raw Joys.send(comp_name, *args, **kwargs, &block)
|
25
|
+
end
|
26
|
+
def layout(name, &block)
|
27
|
+
layout_lambda = Joys.layouts[name]
|
28
|
+
raise ArgumentError, "Layout `#{name}` not registered" unless layout_lambda
|
29
|
+
raw layout_lambda.call(self, &block)
|
30
|
+
end
|
31
|
+
def styles(scoped: false, &block)
|
32
|
+
context_name = Joys.current_component
|
33
|
+
context_name ||= Joys.current_page
|
34
|
+
context_name ||= @current_page
|
35
|
+
return nil unless context_name
|
36
|
+
return nil if Joys.compiled_styles[context_name]
|
37
|
+
@style_base_css = []
|
38
|
+
@style_media_queries = {}
|
39
|
+
@style_scoped = scoped
|
40
|
+
instance_eval(&block)
|
41
|
+
Joys::Styles.compile_component_styles(
|
42
|
+
context_name,
|
43
|
+
@style_base_css,
|
44
|
+
@style_media_queries,
|
45
|
+
@style_scoped
|
46
|
+
)
|
47
|
+
if context_name&.start_with?('page_')
|
48
|
+
@used_components ||= Set.new
|
49
|
+
@used_components.add(context_name)
|
50
|
+
end
|
51
|
+
@style_base_css = @style_media_queries = @style_scoped = nil;nil
|
52
|
+
end
|
53
|
+
def doctype;raw "<!doctype html>";end
|
54
|
+
def css(content);@style_base_css&.push(content);nil;end
|
55
|
+
def media_max(breakpoint, content)
|
56
|
+
return nil unless @style_media_queries
|
57
|
+
key = "m-max-#{breakpoint}"
|
58
|
+
@style_media_queries[key] ||= []
|
59
|
+
@style_media_queries[key] << content;nil
|
60
|
+
end
|
61
|
+
def media_min(breakpoint, content)
|
62
|
+
return nil unless @style_media_queries
|
63
|
+
key = "m-min-#{breakpoint}"
|
64
|
+
@style_media_queries[key] ||= []
|
65
|
+
@style_media_queries[key] << content;nil
|
66
|
+
end
|
67
|
+
def media_minmax(min_bp, max_bp, content)
|
68
|
+
return nil unless @style_media_queries
|
69
|
+
key = "m-minmax-#{min_bp}-#{max_bp}"
|
70
|
+
@style_media_queries[key] ||= []
|
71
|
+
@style_media_queries[key] << content;nil
|
72
|
+
end
|
73
|
+
def container_min(size, content)
|
74
|
+
return nil unless @style_media_queries
|
75
|
+
key = "c-min-#{size}"
|
76
|
+
@style_media_queries[key] ||= []
|
77
|
+
@style_media_queries[key] << content;nil
|
78
|
+
end
|
79
|
+
def container_max(size, content)
|
80
|
+
return nil unless @style_media_queries
|
81
|
+
key = "c-max-#{size}"
|
82
|
+
@style_media_queries[key] ||= []
|
83
|
+
@style_media_queries[key] << content;nil
|
84
|
+
end
|
85
|
+
def container_minmax(min_size, max_size, content)
|
86
|
+
return nil unless @style_media_queries
|
87
|
+
key = "c-minmax-#{min_size}-#{max_size}"
|
88
|
+
@style_media_queries[key] ||= []
|
89
|
+
@style_media_queries[key] << content;nil
|
90
|
+
end
|
91
|
+
def named_container_min(name, size, content)
|
92
|
+
return nil unless @style_media_queries
|
93
|
+
key = "c-#{name}-min-#{size}"
|
94
|
+
@style_media_queries[key] ||= []
|
95
|
+
@style_media_queries[key] << content;nil
|
96
|
+
end
|
97
|
+
def named_container_max(name, size, content)
|
98
|
+
return nil unless @style_media_queries
|
99
|
+
key = "c-#{name}-max-#{size}"
|
100
|
+
@style_media_queries[key] ||= []
|
101
|
+
@style_media_queries[key] << content;nil
|
102
|
+
end
|
103
|
+
def named_container_minmax(name, min_size, max_size, content)
|
104
|
+
return nil unless @style_media_queries
|
105
|
+
key = "c-#{name}-minmax-#{min_size}-#{max_size}"
|
106
|
+
@style_media_queries[key] ||= []
|
107
|
+
@style_media_queries[key] << content;nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/joys/styles.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# lib/joys/styles.rb - Simplified Component Styling
|
3
|
+
require 'fileutils'
|
4
|
+
require 'set'
|
5
|
+
module Joys
|
6
|
+
module Styles
|
7
|
+
def self.compile_component_styles(comp_name, base_css, media_queries, scoped)
|
8
|
+
scope_prefix = scoped ? ".#{comp_name.tr('_', '-')} " : ""
|
9
|
+
processed_base_css = base_css.map { |css| scope_css(css, scope_prefix) }
|
10
|
+
processed_media_queries = {}
|
11
|
+
|
12
|
+
# FIX: Actually iterate over the media_queries parameter
|
13
|
+
media_queries.each do |key, css_rules|
|
14
|
+
scoped_rules = css_rules.map { |css| scope_css(css, scope_prefix) }
|
15
|
+
processed_media_queries[key] = scoped_rules
|
16
|
+
end
|
17
|
+
|
18
|
+
Joys.compiled_styles[comp_name] = {
|
19
|
+
base_css: processed_base_css,
|
20
|
+
media_queries: processed_media_queries,
|
21
|
+
container_queries: {} # Keep this for compatibility, but media_queries contains all now
|
22
|
+
}.freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def self.render_consolidated_styles(used_components)
|
28
|
+
return "" if used_components.empty?
|
29
|
+
cache_key = used_components.to_a.sort.join(',')
|
30
|
+
return Joys.consolidated_cache[cache_key] if Joys.consolidated_cache.key?(cache_key)
|
31
|
+
buffer = String.new(capacity: 4096)
|
32
|
+
min_queries = Hash.new { |h,k| h[k] = [] }
|
33
|
+
max_queries = Hash.new { |h,k| h[k] = [] }
|
34
|
+
minmax_queries = []
|
35
|
+
container_min_queries = Hash.new { |h,k| h[k] = [] }
|
36
|
+
container_max_queries = Hash.new { |h,k| h[k] = [] }
|
37
|
+
container_minmax_queries = []
|
38
|
+
container_named_queries = Hash.new { |h,k| h[k] = {min: Hash.new { |h,k| h[k] = [] }, max: Hash.new { |h,k| h[k] = [] }, minmax: []} }
|
39
|
+
seen_base = Set.new
|
40
|
+
|
41
|
+
used_components.each do |comp|
|
42
|
+
styles = Joys.compiled_styles[comp] || next
|
43
|
+
styles[:base_css].each do |rule|
|
44
|
+
next if seen_base.include?(rule)
|
45
|
+
buffer << rule
|
46
|
+
seen_base << rule
|
47
|
+
end
|
48
|
+
styles[:media_queries].each do |key, rules|
|
49
|
+
case key
|
50
|
+
# FIX: Updated regex patterns to match the actual key formats
|
51
|
+
when /^m-min-(\d+)$/
|
52
|
+
min_queries[$1.to_i].concat(rules)
|
53
|
+
when /^m-max-(\d+)$/
|
54
|
+
max_queries[$1.to_i].concat(rules)
|
55
|
+
when /^m-minmax-(\d+)-(\d+)$/
|
56
|
+
minmax_queries << { min: $1.to_i, max: $2.to_i, css: rules }
|
57
|
+
when /^c-min-(\d+)$/
|
58
|
+
container_min_queries[$1.to_i].concat(rules)
|
59
|
+
when /^c-max-(\d+)$/
|
60
|
+
container_max_queries[$1.to_i].concat(rules)
|
61
|
+
when /^c-minmax-(\d+)-(\d+)$/
|
62
|
+
container_minmax_queries << { min: $1.to_i, max: $2.to_i, css: rules }
|
63
|
+
when /^c-([^-]+)-min-(\d+)$/
|
64
|
+
name, size = $1, $2.to_i
|
65
|
+
container_named_queries[name][:min][size].concat(rules)
|
66
|
+
when /^c-([^-]+)-max-(\d+)$/
|
67
|
+
name, size = $1, $2.to_i
|
68
|
+
container_named_queries[name][:max][size].concat(rules)
|
69
|
+
when /^c-([^-]+)-minmax-(\d+)-(\d+)$/
|
70
|
+
name, min_size, max_size = $1, $2.to_i, $3.to_i
|
71
|
+
container_named_queries[name][:minmax] << { min: min_size, max: max_size, css: rules }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
max_queries.sort.reverse.each do |bp, rules|
|
77
|
+
buffer << "@media (max-width: #{bp}px){"
|
78
|
+
rules.uniq.each { |r| buffer << r }
|
79
|
+
buffer << "}"
|
80
|
+
end
|
81
|
+
min_queries.sort.each do |bp, rules|
|
82
|
+
buffer << "@media (min-width: #{bp}px){"
|
83
|
+
rules.uniq.each { |r| buffer << r }
|
84
|
+
buffer << "}"
|
85
|
+
end
|
86
|
+
minmax_queries.sort_by { |q| [q[:min], q[:max]] }.each do |q|
|
87
|
+
buffer << "@media (min-width: #{q[:min]}px) and (max-width: #{q[:max]}px){"
|
88
|
+
q[:css].uniq.each { |r| buffer << r }
|
89
|
+
buffer << "}"
|
90
|
+
end
|
91
|
+
container_max_queries.sort.reverse.each do |size, rules|
|
92
|
+
buffer << "@container (max-width: #{size}px){"
|
93
|
+
rules.uniq.each { |r| buffer << r }
|
94
|
+
buffer << "}"
|
95
|
+
end
|
96
|
+
container_min_queries.sort.each do |size, rules|
|
97
|
+
buffer << "@container (min-width: #{size}px){"
|
98
|
+
rules.uniq.each { |r| buffer << r }
|
99
|
+
buffer << "}"
|
100
|
+
end
|
101
|
+
container_minmax_queries.sort_by { |q| [q[:min], q[:max]] }.each do |q|
|
102
|
+
buffer << "@container (min-width: #{q[:min]}px) and (max-width: #{q[:max]}px){"
|
103
|
+
q[:css].uniq.each { |r| buffer << r }
|
104
|
+
buffer << "}"
|
105
|
+
end
|
106
|
+
container_named_queries.each do |name, queries|
|
107
|
+
queries[:max].sort.reverse.each do |size, rules|
|
108
|
+
buffer << "@container #{name} (max-width: #{size}px){"
|
109
|
+
rules.uniq.each { |r| buffer << r }
|
110
|
+
buffer << "}"
|
111
|
+
end
|
112
|
+
queries[:min].sort.each do |size, rules|
|
113
|
+
buffer << "@container #{name} (min-width: #{size}px){"
|
114
|
+
rules.uniq.each { |r| buffer << r }
|
115
|
+
buffer << "}"
|
116
|
+
end
|
117
|
+
queries[:minmax].sort_by { |q| [q[:min], q[:max]] }.each do |q|
|
118
|
+
buffer << "@container #{name} (min-width: #{q[:min]}px) and (max-width: #{q[:max]}px){"
|
119
|
+
q[:css].uniq.each { |r| buffer << r }
|
120
|
+
buffer << "}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
buffer.freeze.tap { |css| Joys.consolidated_cache[cache_key] = css }
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.render_external_styles(used_components)
|
127
|
+
return "" if used_components.empty?
|
128
|
+
cache_key = used_components.to_a.sort.join(',')
|
129
|
+
css_filename = "#{cache_key}.css"
|
130
|
+
#css_filename = "#{Digest::SHA256.hexdigest(cache_key)[0..12]}.css"
|
131
|
+
css_path = Joys.css_path || "public/css"
|
132
|
+
full_path = File.join(css_path, css_filename)
|
133
|
+
FileUtils.mkdir_p(css_path)
|
134
|
+
unless File.exist?(full_path)
|
135
|
+
css_content = render_consolidated_styles(used_components)
|
136
|
+
File.write(full_path, css_content)
|
137
|
+
end
|
138
|
+
"<link rel=\"stylesheet\" href=\"/css/#{css_filename}\">"
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.scope_css(css, scope_prefix)
|
142
|
+
return css if scope_prefix.empty?
|
143
|
+
selectors = css.split(',')
|
144
|
+
scoped_selectors = selectors.map do |selector|
|
145
|
+
selector.strip!
|
146
|
+
if match = selector.match(/^\s*\.([a-zA-Z][\w-]*)/)
|
147
|
+
#if selector.match(/^\s*\.([a-zA-Z][\w-]*)/)
|
148
|
+
"#{scope_prefix}#{selector}"
|
149
|
+
else
|
150
|
+
selector
|
151
|
+
end
|
152
|
+
end
|
153
|
+
scoped_selectors.join(', ')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
data/lib/joys/tags.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# lib/joys/tags.rb
|
2
|
+
module Joys
|
3
|
+
module Tags
|
4
|
+
VOID_TAGS = %w[area base br col embed hr img input link meta param source track wbr].freeze
|
5
|
+
TAGS = %w[
|
6
|
+
a abbr address article aside audio b bdi bdo blockquote body button canvas
|
7
|
+
caption cite code colgroup data datalist dd del details dfn dialog div dl dt
|
8
|
+
em fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup
|
9
|
+
html i iframe ins kbd label legend li main map mark menu meter nav noscript
|
10
|
+
object ol optgroup option output p picture pre progress q rp rt ruby s samp
|
11
|
+
script section select small span strong style sub summary sup table tbody td
|
12
|
+
template textarea tfoot th thead time title tr u ul var video
|
13
|
+
].freeze
|
14
|
+
BOOLEAN_ATTRS = %w[
|
15
|
+
disabled readonly read_only multiple checked selected
|
16
|
+
auto_focus autofocus no_validate novalidate form_novalidate formnovalidate
|
17
|
+
hidden required open reversed scoped seamless muted auto_play autoplay
|
18
|
+
controls loop default inert item_scope itemscope
|
19
|
+
].to_set.freeze
|
20
|
+
|
21
|
+
CLS = ' class="'.freeze
|
22
|
+
QUO = '"'.freeze
|
23
|
+
BC = '>'.freeze
|
24
|
+
BO = '<'.freeze
|
25
|
+
GTS = '</'.freeze
|
26
|
+
EQ = '="'.freeze
|
27
|
+
|
28
|
+
VOID_TAGS.each do |tag|
|
29
|
+
define_method(tag) do |cs: nil, **attrs|
|
30
|
+
class_string = case cs
|
31
|
+
when String, Symbol then cs.to_s
|
32
|
+
when Array then cs.compact.reject { |c| c == false }.map(&:to_s).join(" ")
|
33
|
+
else ""
|
34
|
+
end
|
35
|
+
|
36
|
+
@bf << BO << tag
|
37
|
+
@bf << CLS << class_string << QUO unless class_string.empty?
|
38
|
+
|
39
|
+
attrs.each do |k, v|
|
40
|
+
key = k.to_s
|
41
|
+
|
42
|
+
if v.is_a?(Hash) && (key == "data" || key.start_with?("aria"))
|
43
|
+
prefix = key.tr('_', '-')
|
44
|
+
v.each do |sk, sv|
|
45
|
+
next if sv.nil? || sv == false # Skip nil and boolean false
|
46
|
+
attr_name = "#{prefix}-#{sk.to_s.tr('_', '-')}"
|
47
|
+
@bf << " #{attr_name}#{EQ}#{CGI.escapeHTML(sv.to_s)}#{QUO}"
|
48
|
+
end
|
49
|
+
else
|
50
|
+
next if v.nil?
|
51
|
+
|
52
|
+
if BOOLEAN_ATTRS.include?(key)
|
53
|
+
@bf << " #{key.tr('_', '')}" if v
|
54
|
+
elsif v == false
|
55
|
+
next
|
56
|
+
else
|
57
|
+
attr_name = key.tr('_', '-')
|
58
|
+
@bf << " #{attr_name}#{EQ}#{CGI.escapeHTML(v.to_s)}#{QUO}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@bf << BC
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
TAGS.each do |tag|
|
69
|
+
define_method(tag) do |content = nil, cs: nil, raw: false, **attrs, &block|
|
70
|
+
class_string = case cs
|
71
|
+
when String, Symbol then cs.to_s
|
72
|
+
when Array then cs.compact.reject { |c| c == false }.map(&:to_s).join(" ")
|
73
|
+
else ""
|
74
|
+
end
|
75
|
+
|
76
|
+
@bf << BO << tag
|
77
|
+
@bf << CLS << class_string << QUO unless class_string.empty?
|
78
|
+
|
79
|
+
attrs.each do |k, v|
|
80
|
+
key = k.to_s
|
81
|
+
|
82
|
+
if v.is_a?(Hash) && (key == "data" || key.start_with?("aria"))
|
83
|
+
prefix = key.tr('_', '-')
|
84
|
+
v.each do |sk, sv|
|
85
|
+
next if sv.nil? || sv == false # Skip nil and boolean false
|
86
|
+
attr_name = "#{prefix}-#{sk.to_s.tr('_', '-')}"
|
87
|
+
@bf << " #{attr_name}#{EQ}#{CGI.escapeHTML(sv.to_s)}#{QUO}"
|
88
|
+
end
|
89
|
+
else
|
90
|
+
next if v.nil?
|
91
|
+
|
92
|
+
if BOOLEAN_ATTRS.include?(key)
|
93
|
+
@bf << " #{key.tr('_', '')}" if v
|
94
|
+
elsif v == false
|
95
|
+
next
|
96
|
+
else
|
97
|
+
attr_name = key.tr('_', '-')
|
98
|
+
@bf << " #{attr_name}#{EQ}#{CGI.escapeHTML(v.to_s)}#{QUO}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
@bf << BC
|
104
|
+
|
105
|
+
if block
|
106
|
+
instance_eval(&block)
|
107
|
+
elsif content
|
108
|
+
@bf << (raw ? content.to_s : CGI.escapeHTML(content.to_s))
|
109
|
+
end
|
110
|
+
|
111
|
+
@bf << GTS << tag << BC
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
define_method("#{tag}!") do |content = nil, cs: nil, **attrs, &block|
|
116
|
+
send(tag, content, cs: cs, raw: true, **attrs, &block)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/joys/version.rb
CHANGED
data/lib/joys.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "joys/version"
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
4
|
+
require_relative "joys/core"
|
5
|
+
require_relative "joys/helpers"
|
6
|
+
require_relative "joys/styles"
|
7
|
+
require_relative "joys/tags"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: joys
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Garcia
|
@@ -25,8 +25,14 @@ files:
|
|
25
25
|
- LICENSE.txt
|
26
26
|
- README.md
|
27
27
|
- Rakefile
|
28
|
+
- joys-0.1.0.gem
|
29
|
+
- joys-0.1.1.gem
|
28
30
|
- lib/.DS_Store
|
29
31
|
- lib/joys.rb
|
32
|
+
- lib/joys/core.rb
|
33
|
+
- lib/joys/helpers.rb
|
34
|
+
- lib/joys/styles.rb
|
35
|
+
- lib/joys/tags.rb
|
30
36
|
- lib/joys/version.rb
|
31
37
|
- sig/joys.rbs
|
32
38
|
homepage: https://github.com/activestylus/joys.git
|