joys 0.1.1 → 0.1.3
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 +13 -0
- data/README.md +335 -160
- data/joys-0.1.0.gem +0 -0
- data/joys-0.1.1.gem +0 -0
- data/joys-0.1.2.gem +0 -0
- data/lib/.DS_Store +0 -0
- data/lib/joys/cli.rb +1554 -0
- data/lib/joys/config.rb +133 -0
- data/lib/joys/core.rb +189 -0
- data/lib/joys/data.rb +477 -0
- data/lib/joys/helpers.rb +138 -0
- data/lib/joys/ssg.rb +597 -0
- data/lib/joys/styles.rb +156 -0
- data/lib/joys/tags.rb +124 -0
- data/lib/joys/toys.rb +328 -0
- data/lib/joys/version.rb +1 -1
- data/lib/joys.rb +5 -5
- metadata +13 -2
- data/.DS_Store +0 -0
data/lib/joys/config.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Joys
|
3
|
+
def self.html(&block)
|
4
|
+
renderer = Object.new
|
5
|
+
renderer.extend(Render::Helpers)
|
6
|
+
renderer.extend(Tags)
|
7
|
+
renderer.instance_variable_set(:@bf, String.new)
|
8
|
+
renderer.instance_variable_set(:@current_page, "page_standalone")
|
9
|
+
renderer.instance_variable_set(:@used_components, Set.new)
|
10
|
+
renderer.instance_variable_set(:@slots, {})
|
11
|
+
renderer.instance_eval(&block)
|
12
|
+
renderer.instance_variable_get(:@bf)
|
13
|
+
end
|
14
|
+
module Config
|
15
|
+
class << self
|
16
|
+
attr_accessor :env,:pages,:layouts,:components,:css_parts,:helpers,:markup_parser
|
17
|
+
def markup_parser
|
18
|
+
@markup_parser ||= ->(content) { content.to_s }
|
19
|
+
end
|
20
|
+
def env;@env||=ENV['JOYS_ENV']||(defined?(::Rails) ? ::Rails.env.to_s : "development");end
|
21
|
+
def dev?;env!="production";end
|
22
|
+
def pages;@pages||=path("pages");end
|
23
|
+
def layouts;@layouts||=path("layouts");end
|
24
|
+
def components;@components||=path("components");end
|
25
|
+
def css_parts;@css_parts||=path("css");end
|
26
|
+
def helpers;@helpers||=path("helpers");end
|
27
|
+
private
|
28
|
+
def path(t);defined?(::Rails) ? ::Rails.root.join("app/views/joys/#{t}").to_s : "views/joys/#{t}";end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
module Adapters
|
32
|
+
class Base
|
33
|
+
def integrate!;raise NotImplementedError;end
|
34
|
+
def inject_helpers!;raise NotImplementedError;end
|
35
|
+
def controller_context(controller);{};end
|
36
|
+
end
|
37
|
+
class Rails < Base
|
38
|
+
def integrate!
|
39
|
+
return unless defined?(ActionController::Base)
|
40
|
+
ActionController::Base.include(ControllerMethods)
|
41
|
+
inject_helpers!
|
42
|
+
::Rails.application.config.to_prepare do
|
43
|
+
Joys.preload! if ::Rails.env.production?
|
44
|
+
Joys::Render::Helpers.include(::Rails.application.helpers)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
def inject_helpers!
|
48
|
+
Joys::Render::Helpers.include(ActionView::Helpers)
|
49
|
+
Joys::Render::Helpers.module_eval do
|
50
|
+
def request;Thread.current[:joys_request];end
|
51
|
+
def params;Thread.current[:joys_params];end
|
52
|
+
def current_user;Thread.current[:joys_current_user];end
|
53
|
+
def session;Thread.current[:joys_session];end
|
54
|
+
def _(content);raw(content);end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
def controller_context(controller)
|
58
|
+
{
|
59
|
+
joys_request: controller.request,
|
60
|
+
joys_params: controller.params,
|
61
|
+
joys_current_user: (controller.current_user if controller.respond_to?(:current_user)),
|
62
|
+
joys_session: controller.session
|
63
|
+
}
|
64
|
+
end
|
65
|
+
module ControllerMethods
|
66
|
+
def render_joy(path,**locals)
|
67
|
+
context=Joys.adapter.controller_context(self)
|
68
|
+
context.each{|k,v|Thread.current[k]=v}
|
69
|
+
result=Joys.render_joy(path,**locals)
|
70
|
+
render html:result.html_safe,layout:false
|
71
|
+
ensure
|
72
|
+
context&.keys&.each{|k|Thread.current[k]=nil}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
class Sinatra < Base
|
77
|
+
def integrate!;end
|
78
|
+
def inject_helpers!;end
|
79
|
+
end
|
80
|
+
class Hanami < Base
|
81
|
+
def integrate!;end
|
82
|
+
def inject_helpers!;end
|
83
|
+
end
|
84
|
+
class Roda < Base
|
85
|
+
def integrate!;end
|
86
|
+
def inject_helpers!;end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
class << self
|
90
|
+
attr_accessor :adapter,:css_registry
|
91
|
+
def render_joy(path,**locals)
|
92
|
+
reload! if Config.dev?
|
93
|
+
file=File.join(Config.pages,"#{path}.rb")
|
94
|
+
raise "Template not found: #{file}" unless File.exist?(file)
|
95
|
+
locals.each{|k,v|eval("@#{k}=v",binding)}
|
96
|
+
result=eval(File.read(file),binding,file)
|
97
|
+
result.is_a?(String) ? result.freeze : ""
|
98
|
+
end
|
99
|
+
def preload!;load_helpers;load_dir(Config.layouts);load_dir(Config.components);load_css_parts;end
|
100
|
+
def load_helpers
|
101
|
+
return unless Dir.exist?(Config.helpers)
|
102
|
+
Dir.glob("#{Config.helpers}/**/*.rb").each do |f|
|
103
|
+
helper_module = Module.new
|
104
|
+
helper_module.module_eval(File.read(f), f)
|
105
|
+
Joys::Render::Helpers.include(helper_module)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
def load_css_parts
|
109
|
+
return unless Dir.exist?(Config.css_parts)
|
110
|
+
Dir.glob("#{Config.css_parts}/**/*.css").each do |f|
|
111
|
+
relative_path = f.sub("#{Config.css_parts}/", '').sub('.css', '')
|
112
|
+
@css_registry[relative_path] = File.read(f).gsub(/\r\n?|\n/, '')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
def detect_framework!
|
116
|
+
@css_registry={}
|
117
|
+
@adapter=if defined?(ActionController::Base)&&defined?(::Rails)
|
118
|
+
Adapters::Rails.new
|
119
|
+
elsif defined?(Sinatra::Base)
|
120
|
+
Adapters::Sinatra.new
|
121
|
+
elsif defined?(Hanami::Action)
|
122
|
+
Adapters::Hanami.new
|
123
|
+
elsif defined?(Roda)
|
124
|
+
Adapters::Roda.new
|
125
|
+
end
|
126
|
+
@adapter&.integrate!
|
127
|
+
end
|
128
|
+
private
|
129
|
+
def reload!;clear_cache!;@css_registry={};preload!;end
|
130
|
+
def load_dir(dir);Dir.exist?(dir)&&Dir.glob("#{dir}/**/*.rb").each{|f|load f};end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
Joys.detect_framework!
|
data/lib/joys/core.rb
ADDED
@@ -0,0 +1,189 @@
|
|
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,:adapter,:css_registry
|
8
|
+
# def load_css_parts
|
9
|
+
# return unless Dir.exist?(Config.css_parts)
|
10
|
+
# Dir.glob("#{Config.css_parts}/**/*.css").each do |f|
|
11
|
+
# relative_path = f.sub("#{Config.css_parts}/", '').sub('.css', '')
|
12
|
+
# @css_registry[relative_path] = File.read(f).gsub(/\r?\n/, "")
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
end
|
16
|
+
def self.make(name, *args, **kwargs, &block)
|
17
|
+
Joys.define :comp, name, *args, **kwargs, &block
|
18
|
+
end
|
19
|
+
def self.reset!
|
20
|
+
@cache.clear
|
21
|
+
@templates.clear
|
22
|
+
@compiled_styles.clear
|
23
|
+
@consolidated_cache.clear
|
24
|
+
@layouts.clear
|
25
|
+
@current_component = nil
|
26
|
+
@current_page = nil
|
27
|
+
end
|
28
|
+
def self.cache(cache_id, *args, &block);cache_key = [cache_id, args.hash];@cache[cache_key] ||= block.call;end
|
29
|
+
def self.define(type, name, &template)
|
30
|
+
full_name = "#{type}_#{name}";@templates[full_name] = template
|
31
|
+
case type
|
32
|
+
when :layout
|
33
|
+
@layouts[name] = Joys::Render.layout(&template)
|
34
|
+
define_singleton_method(full_name) { @layouts[name] }
|
35
|
+
when :page
|
36
|
+
define_singleton_method(full_name) do |**locals|
|
37
|
+
old_component = @current_component
|
38
|
+
old_page = @current_page
|
39
|
+
@current_page = full_name
|
40
|
+
@current_component = nil
|
41
|
+
result = cache(full_name, locals.hash) do
|
42
|
+
renderer = Object.new
|
43
|
+
renderer.extend(Render::Helpers)
|
44
|
+
renderer.extend(Tags)
|
45
|
+
locals.each { |k, v| renderer.instance_variable_set("@#{k}", v) }
|
46
|
+
renderer.instance_eval do
|
47
|
+
@bf = String.new(capacity: 8192)
|
48
|
+
@slots={}
|
49
|
+
@used_components = Set.new
|
50
|
+
@current_page = full_name # Pass page context to renderer
|
51
|
+
end
|
52
|
+
renderer.instance_exec(&template)
|
53
|
+
renderer.instance_variable_get(:@bf)
|
54
|
+
end
|
55
|
+
@current_component = old_component
|
56
|
+
@current_page = old_page
|
57
|
+
result.freeze
|
58
|
+
end
|
59
|
+
else
|
60
|
+
define_singleton_method(full_name) do |*args|
|
61
|
+
old_component = @current_component
|
62
|
+
@current_component = full_name if type == :comp
|
63
|
+
result = cache(full_name, *args) do
|
64
|
+
# This call now returns a hash, and the hash will be cached.
|
65
|
+
Joys::Render.compile(full_name) { instance_exec(*args, &template) }
|
66
|
+
end
|
67
|
+
@current_component = old_component
|
68
|
+
result
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
def self.clear_cache!
|
73
|
+
@cache.clear
|
74
|
+
@compiled_styles.clear
|
75
|
+
@consolidated_cache.clear
|
76
|
+
end
|
77
|
+
def self.page(name, **locals)
|
78
|
+
page_name = "page_#{name}"
|
79
|
+
renderer = Object.new
|
80
|
+
renderer.extend(Render::Helpers)
|
81
|
+
renderer.extend(Tags)
|
82
|
+
locals.each { |k, v| renderer.instance_variable_set("@#{k}", v) }
|
83
|
+
renderer.instance_eval do
|
84
|
+
@bf = String.new(capacity: 8192)
|
85
|
+
@slots={}
|
86
|
+
@used_components = Set.new
|
87
|
+
@current_page = page_name
|
88
|
+
@used_components.add(page_name) if Joys.compiled_styles[page_name]
|
89
|
+
end
|
90
|
+
page_template = @templates[page_name]
|
91
|
+
raise "No page template defined for #{name}" unless page_template
|
92
|
+
old_page = @current_page
|
93
|
+
@current_page = page_name
|
94
|
+
renderer.instance_exec(&page_template)
|
95
|
+
@current_page = old_page
|
96
|
+
renderer.instance_variable_get(:@bf).freeze
|
97
|
+
end
|
98
|
+
def self.comp(name, *args, **locals, &block)
|
99
|
+
renderer = Object.new
|
100
|
+
renderer.extend(Render::Helpers)
|
101
|
+
renderer.extend(Tags)
|
102
|
+
locals.each { |k, v| renderer.instance_variable_set("@#{k}", v) }
|
103
|
+
renderer.instance_eval do
|
104
|
+
@bf = String.new(capacity: 8192)
|
105
|
+
@slots = {}
|
106
|
+
@used_components = Set.new
|
107
|
+
end
|
108
|
+
|
109
|
+
comp_template = @templates["comp_#{name}"]
|
110
|
+
raise "No comp template defined for #{name}" unless comp_template
|
111
|
+
|
112
|
+
old_component = @current_component
|
113
|
+
@current_component = "comp_#{name}"
|
114
|
+
|
115
|
+
# Pass the block as a regular argument to the template
|
116
|
+
renderer.instance_exec(*args, block, &comp_template)
|
117
|
+
|
118
|
+
@current_component = old_component
|
119
|
+
renderer.instance_variable_get(:@bf).freeze
|
120
|
+
end
|
121
|
+
def self.html(&block)
|
122
|
+
# Clear any previous tracking
|
123
|
+
clear_tracked_components
|
124
|
+
|
125
|
+
renderer = Object.new
|
126
|
+
renderer.extend(Render::Helpers)
|
127
|
+
renderer.extend(Tags)
|
128
|
+
renderer.instance_variable_set(:@bf, String.new)
|
129
|
+
renderer.instance_variable_set(:@current_page, "page_standalone")
|
130
|
+
renderer.instance_variable_set(:@used_components, Set.new)
|
131
|
+
renderer.instance_variable_set(:@slots, {})
|
132
|
+
|
133
|
+
renderer.instance_eval(&block)
|
134
|
+
|
135
|
+
# Merge globally tracked components
|
136
|
+
tracked = get_tracked_components
|
137
|
+
used = renderer.instance_variable_get(:@used_components)
|
138
|
+
used.merge(tracked) if tracked
|
139
|
+
|
140
|
+
# Clear tracking after use
|
141
|
+
clear_tracked_components
|
142
|
+
|
143
|
+
renderer.instance_variable_get(:@bf)
|
144
|
+
end
|
145
|
+
module Render
|
146
|
+
def self.compile(context_name = nil, &block)
|
147
|
+
context = Object.new; context.extend(Helpers); context.extend(Tags)
|
148
|
+
context.instance_eval { @bf = String.new(capacity: 8192); @slots={}; @used_components = Set.new }
|
149
|
+
context.instance_variable_set(:@current_page, context_name) if context_name
|
150
|
+
context.instance_eval(&block)
|
151
|
+
|
152
|
+
# CHANGE: Return a hash containing both html and the used components set
|
153
|
+
{
|
154
|
+
html: context.instance_variable_get(:@bf).freeze,
|
155
|
+
components: context.instance_variable_get(:@used_components)
|
156
|
+
}
|
157
|
+
end
|
158
|
+
def self.layout(&layout_block)
|
159
|
+
template = Object.new;template.extend(Helpers);template.extend(Tags)
|
160
|
+
template.instance_eval { @bf = String.new;@slots={};@used_components = Set.new }
|
161
|
+
template.define_singleton_method(:pull) { |name = :main| @bf << "<!--SLOT:#{name}-->"; nil }
|
162
|
+
template.instance_eval(&layout_block)
|
163
|
+
precompiled = template.instance_variable_get(:@bf).freeze
|
164
|
+
->(context, &content_block) {
|
165
|
+
context.instance_eval(&content_block) if content_block
|
166
|
+
slots = context.instance_variable_get(:@slots) || {}
|
167
|
+
used_components = context.instance_variable_get(:@used_components)
|
168
|
+
if used_components && !used_components.empty?
|
169
|
+
auto_styles = Joys::Styles.render_consolidated_styles(used_components)
|
170
|
+
else
|
171
|
+
auto_styles = ""
|
172
|
+
end
|
173
|
+
slot_regex = /<!--SLOT:(.*?)-->/.freeze
|
174
|
+
result = precompiled.gsub(slot_regex) do |match|
|
175
|
+
slot_name = $1.to_sym
|
176
|
+
slots[slot_name] || ""
|
177
|
+
end
|
178
|
+
if result.include?('<!--STYLES-->')
|
179
|
+
result = result.gsub(/<!--STYLES-->/, auto_styles)
|
180
|
+
end
|
181
|
+
if result.include?('<!--EXTERNAL_STYLES-->')
|
182
|
+
external_styles = Joys::Styles.render_external_styles(used_components)
|
183
|
+
result = result.gsub(/<!--EXTERNAL_STYLES-->/, external_styles)
|
184
|
+
end
|
185
|
+
result
|
186
|
+
}
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|