joys 0.1.2 → 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 +8 -0
- data/README.md +321 -150
- 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 +70 -42
- data/lib/joys/data.rb +477 -0
- data/lib/joys/helpers.rb +36 -9
- data/lib/joys/ssg.rb +597 -0
- data/lib/joys/tags.rb +5 -1
- data/lib/joys/toys.rb +328 -0
- data/lib/joys/version.rb +1 -1
- data/lib/joys.rb +2 -1
- metadata +7 -2
- data/.DS_Store +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e61c772b1a54599aa640ae7185d7c555584569b42a828a622585fe1926c5c875
|
4
|
+
data.tar.gz: 3386e6f05c3bf96bda1b3c89ee060896a4bf6d652a81044729929cb32ef21fdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b64267c0567828a6ef1ddf5682336f03e33e06a7e2b4ed1ef4fabf0712a835be9ab1f69c449dc6c8bfb172b1bcfb876f00bb9a3f424bf0a6d6537d67fe39e26
|
7
|
+
data.tar.gz: 307da8295d8df3b53cac60d55c3550c42e2b3926e9b5de5629011f814acc6bda73fd97f541fc95afe4f0b76ff16441e92e06f27f133533b102cce1d87b13f69c
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -49,6 +49,9 @@ p { txt user_input } # "<script>alert('xss')</script>" becomes safe text
|
|
49
49
|
# raw outputs HTML (use with caution)
|
50
50
|
div { raw markdown_to_html(content) }
|
51
51
|
|
52
|
+
# raw shortcut is available for those who find it tedious
|
53
|
+
div { _ markdown_to_html(content) }
|
54
|
+
|
52
55
|
# Pass raw: true to any tag
|
53
56
|
div(content, raw: true) # content renders as HTML
|
54
57
|
```
|
@@ -243,66 +246,6 @@ Joys.define(:layout, :main) do
|
|
243
246
|
}
|
244
247
|
end
|
245
248
|
```
|
246
|
-
|
247
|
-
---
|
248
|
-
|
249
|
-
## Rails Integration
|
250
|
-
|
251
|
-
### Setup
|
252
|
-
|
253
|
-
```ruby
|
254
|
-
# config/initializers/joys.rb
|
255
|
-
require 'joys/rails'
|
256
|
-
```
|
257
|
-
|
258
|
-
### File Structure
|
259
|
-
|
260
|
-
```
|
261
|
-
app/views/joys/
|
262
|
-
├── layouts/
|
263
|
-
│ └── application.rb
|
264
|
-
├── comps/
|
265
|
-
│ ├── navbar.rb
|
266
|
-
│ └── user_card.rb
|
267
|
-
└── pages/
|
268
|
-
├── dashboard.rb
|
269
|
-
└── users_show.rb
|
270
|
-
```
|
271
|
-
|
272
|
-
### Rails Helpers Work Seamlessly
|
273
|
-
|
274
|
-
Joys is fully compatible with Rails `ActiveSupport::SafeBuffer`, so you can use all Rails helpers seamlessly.
|
275
|
-
|
276
|
-
```ruby
|
277
|
-
Joys.define(:page, :users_edit) do
|
278
|
-
layout(:main) {
|
279
|
-
push(:main) {
|
280
|
-
# Rails form helpers just work
|
281
|
-
form_with(model: @user) do |f|
|
282
|
-
div(cs: "field") {
|
283
|
-
f.label(:name)
|
284
|
-
f.text_field(:name, class: "input")
|
285
|
-
}
|
286
|
-
f.submit("Save", class: "btn btn-primary")
|
287
|
-
end
|
288
|
-
# As do route helpers
|
289
|
-
a("Back", href: users_path, cs: "link")
|
290
|
-
}
|
291
|
-
}
|
292
|
-
end
|
293
|
-
```
|
294
|
-
|
295
|
-
Rendering from controllers could not be more simple!
|
296
|
-
|
297
|
-
```
|
298
|
-
class UsersController < ApplicationController
|
299
|
-
def show
|
300
|
-
@user = User.find(params[:id])
|
301
|
-
render_joys :users_show # renders from #{Rails.root}/app/views/joys/users_show.rb
|
302
|
-
end
|
303
|
-
end
|
304
|
-
```
|
305
|
-
|
306
249
|
---
|
307
250
|
|
308
251
|
## Real-World Example
|
@@ -819,130 +762,358 @@ Joys is designed to stay out of your way. At its core, it leans functional — y
|
|
819
762
|
The DSL is the same either way — Joys just adapts.
|
820
763
|
|
821
764
|
|
765
|
+
---
|
766
|
+
|
767
|
+
# Joys Rails Integration
|
768
|
+
|
769
|
+
## Installation
|
770
|
+
|
771
|
+
Add to your Rails app's initializer:
|
772
|
+
|
822
773
|
```ruby
|
823
|
-
|
824
|
-
|
774
|
+
require 'joys'
|
775
|
+
```
|
825
776
|
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
777
|
+
Auto-detects Rails and configures integration automatically.
|
778
|
+
|
779
|
+
## How It Works
|
780
|
+
|
781
|
+
- **Framework Detection**: Checks for `ActionController::Base` and `Rails`
|
782
|
+
- **Helper Injection**: Adds all `ActionView::Helpers` to `Joys::Render::Helpers`
|
783
|
+
- **Context Injection**: Provides `request`, `params`, `current_user`, `session` in templates
|
784
|
+
- **Controller Integration**: Adds `render_joy` method to all controllers
|
785
|
+
|
786
|
+
## Directory Structure
|
787
|
+
|
788
|
+
```
|
789
|
+
app/views/joys/
|
790
|
+
├── layouts/ # Auto-loaded at boot (production)
|
791
|
+
├── components/ # Auto-loaded at boot (production)
|
792
|
+
└── pages/ # Loaded on-demand
|
793
|
+
```
|
794
|
+
|
795
|
+
## Configuration
|
796
|
+
|
797
|
+
```ruby
|
798
|
+
# Optional - defaults shown
|
799
|
+
Joys::Config.env = "development" # or Rails.env
|
800
|
+
Joys::Config.pages = "app/views/joys/pages"
|
801
|
+
Joys::Config.layouts = "app/views/joys/layouts"
|
802
|
+
Joys::Config.components = "app/views/joys/components"
|
803
|
+
```
|
804
|
+
|
805
|
+
## Controller Usage
|
806
|
+
|
807
|
+
```ruby
|
808
|
+
class HomeController < ApplicationController
|
809
|
+
def index
|
810
|
+
render_joy :index # loads app/views/joys/pages/index.rb
|
811
|
+
end
|
812
|
+
|
813
|
+
def show
|
814
|
+
render_joy "users/show", user: User.find(params[:id])
|
831
815
|
end
|
832
816
|
end
|
817
|
+
```
|
833
818
|
|
834
|
-
|
819
|
+
## Page Templates
|
820
|
+
|
821
|
+
### Functional Pattern
|
822
|
+
|
823
|
+
```ruby
|
824
|
+
# app/views/joys/pages/index.rb
|
825
|
+
data = {title: "Home", nav: link_to("About", "/about")}.freeze
|
826
|
+
|
827
|
+
Joys.html {
|
828
|
+
layout(:main) {
|
829
|
+
push(:title) { txt data[:title] }
|
830
|
+
push(:body) { _ data[:nav] }
|
831
|
+
}
|
832
|
+
}
|
833
|
+
```
|
834
|
+
|
835
|
+
### Class Pattern
|
836
|
+
|
837
|
+
```ruby
|
838
|
+
# app/views/joys/pages/index.rb
|
839
|
+
class IndexPage
|
840
|
+
def initialize
|
841
|
+
@data = {title: "Home"}.freeze
|
842
|
+
end
|
843
|
+
|
835
844
|
def render
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
845
|
+
data = @data
|
846
|
+
Joys.html {
|
847
|
+
layout(:main) {
|
848
|
+
push(:title) { txt data[:title] }
|
849
|
+
push(:body) { _ link_to("About", "/about") }
|
841
850
|
}
|
842
851
|
}
|
843
852
|
end
|
844
853
|
end
|
845
854
|
|
846
|
-
|
847
|
-
# => <html><head><title>My App</title></head><body><div class="dashboard"><h1>Dashboard</h1><p>Rendered through OOP inheritance.</p><a class="btn" href="/logout">Logout</a></div></body></html>
|
855
|
+
IndexPage.new.render
|
848
856
|
```
|
849
857
|
|
850
|
-
|
858
|
+
## Rails Helpers
|
851
859
|
|
852
|
-
|
853
|
-
## 🧩 Bespoke Page Architecture: Content-Driven Flexibility
|
860
|
+
All Rails helpers available directly:
|
854
861
|
|
855
|
-
|
862
|
+
```ruby
|
863
|
+
Joys.html {
|
864
|
+
layout(:main) {
|
865
|
+
push(:body) {
|
866
|
+
_ link_to("Home", root_path)
|
867
|
+
_ image_tag("logo.png", class: "w-8")
|
868
|
+
_ form_with model: Post.new do |f|
|
869
|
+
f.text_field :title
|
870
|
+
end
|
871
|
+
}
|
872
|
+
}
|
873
|
+
}
|
874
|
+
```
|
856
875
|
|
857
|
-
|
876
|
+
## Context Access
|
858
877
|
|
859
|
-
|
878
|
+
```ruby
|
879
|
+
# Available in all templates
|
880
|
+
request.referer
|
881
|
+
params[:id]
|
882
|
+
current_user.name
|
883
|
+
session[:user_id]
|
884
|
+
```
|
885
|
+
|
886
|
+
## Components
|
860
887
|
|
861
888
|
```ruby
|
862
|
-
#
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
cta: "Start Free Trial"
|
868
|
-
},
|
869
|
-
features: {
|
870
|
-
items: [
|
871
|
-
{ title: "Lightning Fast", desc: "10x faster" },
|
872
|
-
{ title: "Secure", desc: "Bank-level security" }
|
873
|
-
]
|
874
|
-
}
|
875
|
-
}.freeze
|
889
|
+
# app/views/joys/components/card.rb
|
890
|
+
Joys.define :comp, :card do |title|
|
891
|
+
styles { css ".card {padding:1rem;border:1px solid #ddd}" }
|
892
|
+
div(cs: "card") { h3 title }
|
893
|
+
end
|
876
894
|
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
push(:main) {
|
881
|
-
raw hero(DATA[:hero])
|
882
|
-
raw features(DATA[:features])
|
883
|
-
|
884
|
-
# Custom sections between data-driven ones
|
885
|
-
div(cs: "demo") {
|
886
|
-
h2("See It In Action")
|
887
|
-
comp(:demo_video)
|
888
|
-
}
|
889
|
-
|
890
|
-
comp(:testimonials) # Reusable global component
|
891
|
-
}
|
892
|
-
}
|
893
|
-
end
|
894
|
-
end
|
895
|
+
# Usage in templates
|
896
|
+
comp :card, "Hello World"
|
897
|
+
```
|
895
898
|
|
896
|
-
|
899
|
+
## Page Components
|
897
900
|
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
901
|
+
For page-specific components:
|
902
|
+
|
903
|
+
```ruby
|
904
|
+
page_comp(:hero) {
|
905
|
+
styles { css ".hero {text-align:center}" }
|
906
|
+
section(cs: "hero") { h1 "Welcome" }
|
907
|
+
}
|
908
|
+
```
|
909
|
+
|
910
|
+
## Layouts
|
911
|
+
|
912
|
+
```ruby
|
913
|
+
# app/views/joys/layouts/main.rb
|
914
|
+
Joys.define :layout, :main do
|
915
|
+
html {
|
916
|
+
head {
|
917
|
+
title { pull(:title) }
|
918
|
+
pull_styles # Auto-compiled CSS
|
906
919
|
}
|
920
|
+
body { pull(:body) }
|
921
|
+
}
|
922
|
+
end
|
923
|
+
```
|
924
|
+
|
925
|
+
## Raw HTML
|
926
|
+
|
927
|
+
Use `_` for unescaped HTML:
|
928
|
+
|
929
|
+
```ruby
|
930
|
+
div {
|
931
|
+
_ "<strong>Bold</strong>"
|
932
|
+
_ link_to("Link", "/")
|
933
|
+
txt "Escaped text" # Auto-escaped
|
934
|
+
}
|
935
|
+
```
|
936
|
+
|
937
|
+
## Data Passing
|
938
|
+
|
939
|
+
```ruby
|
940
|
+
# Controller
|
941
|
+
render_joy :show, user: current_user, posts: @posts
|
942
|
+
|
943
|
+
# Template
|
944
|
+
user_name = @user.name
|
945
|
+
post_count = @posts.count
|
946
|
+
|
947
|
+
Joys.html {
|
948
|
+
layout(:main) {
|
949
|
+
push(:body) { h1 "Hello #{user_name}" }
|
950
|
+
}
|
951
|
+
}
|
952
|
+
```
|
953
|
+
|
954
|
+
## Environment Behavior
|
955
|
+
|
956
|
+
- **Development**: Templates reload on every request
|
957
|
+
- **Production**: Layouts/components preloaded at boot, pages cached after first render
|
958
|
+
|
959
|
+
---
|
960
|
+
|
961
|
+
## Markup Processing in Joys
|
962
|
+
|
963
|
+
Joys includes a powerful shorthand for processing markup content directly within your templates using the `?` suffix on any tag method.
|
964
|
+
|
965
|
+
### Basic Usage
|
966
|
+
|
967
|
+
Any tag method can be followed by `?` to process its content through a markup parser:
|
968
|
+
|
969
|
+
```ruby
|
970
|
+
# Process content with your configured markup parser
|
971
|
+
div?("# Welcome to Joys")
|
972
|
+
# => <div><h1>Welcome to Joys</h1></div>
|
973
|
+
|
974
|
+
p?("This is **bold** and *italic* text")
|
975
|
+
# => <p>This is <strong>bold</strong> and <em>italic</em> text</p>
|
976
|
+
|
977
|
+
article? do
|
978
|
+
h2?("## Getting Started")
|
979
|
+
p?("Learn how to use the `tag?` methods")
|
980
|
+
end
|
981
|
+
```
|
982
|
+
|
983
|
+
### Configuration
|
984
|
+
|
985
|
+
Configure your preferred markup parser globally:
|
986
|
+
|
987
|
+
```ruby
|
988
|
+
# Using Kramdown
|
989
|
+
Joys::Config.markup_parser = ->(content) {
|
990
|
+
Kramdown::Document.new(content).to_html
|
991
|
+
}
|
992
|
+
|
993
|
+
# Using CommonMarker
|
994
|
+
Joys::Config.markup_parser = ->(content) {
|
995
|
+
CommonMarker.render_html(content)
|
996
|
+
}
|
997
|
+
|
998
|
+
# Using any custom parser
|
999
|
+
Joys::Config.markup_parser = ->(content) {
|
1000
|
+
MyCustomParser.process(content)
|
1001
|
+
}
|
1002
|
+
```
|
1003
|
+
|
1004
|
+
### 🎯 Recommended: Sparx Parser
|
1005
|
+
|
1006
|
+
For the best developer experience, we recommend **[Sparx](https://github.com/activestylus/sparx)** - a modern markup language designed specifically for the Joys ecosystem:
|
1007
|
+
|
1008
|
+
```ruby
|
1009
|
+
# Install: gem install sparx
|
1010
|
+
Joys::Config.markup_parser = ->(content) {
|
1011
|
+
Sparx.parse(content)
|
1012
|
+
}
|
1013
|
+
```
|
1014
|
+
|
1015
|
+
#### Why Sparx?
|
1016
|
+
|
1017
|
+
Sparx eliminates the frustrations of traditional Markdown with:
|
1018
|
+
|
1019
|
+
- **Consistent syntax** - No more `**bold**` vs `__bold__` confusion
|
1020
|
+
- **Perfect nesting** - Formatting works reliably in complex content
|
1021
|
+
- **Semantic HTML** - Clean, accessible output throughout
|
1022
|
+
- **URL references** - Define URLs once, use them everywhere
|
1023
|
+
- **Responsive images** - Built-in support for modern image requirements
|
1024
|
+
- **Academic citations** - Proper citation handling out of the box
|
1025
|
+
|
1026
|
+
#### Sparx Examples
|
1027
|
+
|
1028
|
+
```ruby
|
1029
|
+
# Clean, consistent formatting
|
1030
|
+
div? do
|
1031
|
+
p?("*[Bold] and /[italic] with perfect */[nesting]https://example.com")
|
1032
|
+
# => <p><strong>Bold</strong> and <em>italic</em> with perfect <strong><em><a href="https://example.com">nesting</a></em></strong></p>
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
# Responsive images with art direction
|
1036
|
+
div? do
|
1037
|
+
p?("i[Hero]@cdn/hero.jpg 400w|hero@2x.jpg 800w")
|
1038
|
+
# => Generates full <picture> element with responsive sources
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
# Complex content with semantic structure
|
1042
|
+
article? do
|
1043
|
+
p?("+[Technical Details]{:Performance:100x faster parsing:Output:Semantic HTML5}")
|
1044
|
+
# => Creates <details> with definition list inside
|
1045
|
+
end
|
1046
|
+
```
|
1047
|
+
|
1048
|
+
### Comparison with Other Methods
|
1049
|
+
|
1050
|
+
```ruby
|
1051
|
+
# Raw text (no processing)
|
1052
|
+
div("## This won't parse")
|
1053
|
+
# => <div>## This won't parse</div>
|
1054
|
+
|
1055
|
+
# Raw HTML (dangerous with user content)
|
1056
|
+
div!("<h1>Raw HTML</h1>")
|
1057
|
+
# => <div><h1>Raw HTML</h1></div>
|
1058
|
+
|
1059
|
+
# Markup processing (recommended)
|
1060
|
+
div?("## This becomes H1")
|
1061
|
+
# => <div><h1>This becomes H1</h1></div>
|
1062
|
+
```
|
1063
|
+
|
1064
|
+
### Advanced Usage
|
1065
|
+
|
1066
|
+
#### Inline with Other Tags
|
1067
|
+
|
1068
|
+
```ruby
|
1069
|
+
div(cs: "content") do
|
1070
|
+
h1?("# Article Title")
|
1071
|
+
div?(css: "prose") do
|
1072
|
+
p?("First paragraph with *[bold] text")
|
1073
|
+
blockquote?("> Important quote here")
|
1074
|
+
ul?("- Item one\n- Item two\n- Item three")
|
907
1075
|
end
|
1076
|
+
end
|
1077
|
+
```
|
908
1078
|
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
}
|
1079
|
+
#### With Component Composition
|
1080
|
+
|
1081
|
+
```ruby
|
1082
|
+
Joys.register(:comp, :blog_post) do |title:, content:|
|
1083
|
+
article do
|
1084
|
+
header do
|
1085
|
+
h1(title)
|
1086
|
+
time(Time.now.iso8601)
|
1087
|
+
end
|
1088
|
+
div?(cs: "content") { raw content }
|
920
1089
|
end
|
921
1090
|
end
|
1091
|
+
|
1092
|
+
# Usage with Sparx content
|
1093
|
+
Joys.comp(:blog_post,
|
1094
|
+
title: "My Article",
|
1095
|
+
content: Sparx.parse("## Introduction\n\nThis is the *[content]")
|
1096
|
+
)
|
922
1097
|
```
|
923
1098
|
|
924
|
-
###
|
1099
|
+
### Performance Notes
|
925
1100
|
|
926
|
-
|
927
|
-
-
|
928
|
-
-
|
1101
|
+
- Markup parsing happens at **render time**, not registration time
|
1102
|
+
- Processing is **cached** along with the rest of your template
|
1103
|
+
- For maximum performance with user-generated content, consider pre-processing markup outside the template
|
929
1104
|
|
930
|
-
|
931
|
-
- Move sections by reordering method calls
|
932
|
-
- Client requests become 30-second changes
|
1105
|
+
### Security
|
933
1106
|
|
934
|
-
|
935
|
-
- Data-driven sections for consistency
|
936
|
-
- Custom HTML where needed
|
937
|
-
- Reusable global components
|
938
|
-
- Page-specific anonymous components
|
1107
|
+
When processing user-generated content, ensure your markup parser is configured for safe mode:
|
939
1108
|
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
1109
|
+
```ruby
|
1110
|
+
# With Sparx safe mode
|
1111
|
+
Joys::Config.markup_parser = ->(content) {
|
1112
|
+
Sparx.parse(content, safe: true)
|
1113
|
+
}
|
1114
|
+
```
|
944
1115
|
|
945
|
-
|
1116
|
+
The `?` suffix provides a clean, consistent way to integrate markup processing into your Joys templates while maintaining the framework's exceptional performance characteristics.
|
946
1117
|
|
947
1118
|
---
|
948
1119
|
|
data/joys-0.1.2.gem
ADDED
Binary file
|
data/lib/.DS_Store
CHANGED
Binary file
|