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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23119ec444e28d23bfa38b26001df0ad2fad672021083fc3371fd5dd8957bea6
4
- data.tar.gz: e4735595c70399f92886a28b7faeb1f75ac80041d5231cf048800b85ea68d12d
3
+ metadata.gz: e61c772b1a54599aa640ae7185d7c555584569b42a828a622585fe1926c5c875
4
+ data.tar.gz: 3386e6f05c3bf96bda1b3c89ee060896a4bf6d652a81044729929cb32ef21fdd
5
5
  SHA512:
6
- metadata.gz: 05faad732f1786b7138283f616f76afe6fca52c4d57d46085250c12c18ea9ce367e024443e3e45da742dca5fe057416030b6663cee402019d4ef9cef2209d1b5
7
- data.tar.gz: 05c5543a53c6540f3330ca365f0f0dbfc52fa40f028aac7407f7ed48c4cfde8a3e8dd8f451912d7b608242963107fc3b18c8ef968f8474a9a22f73a3a220fb15
6
+ metadata.gz: 6b64267c0567828a6ef1ddf5682336f03e33e06a7e2b4ed1ef4fabf0712a835be9ab1f69c449dc6c8bfb172b1bcfb876f00bb9a3f424bf0a6d6537d67fe39e26
7
+ data.tar.gz: 307da8295d8df3b53cac60d55c3550c42e2b3926e9b5de5629011f814acc6bda73fd97f541fc95afe4f0b76ff16441e92e06f27f133533b102cce1d87b13f69c
data/CHANGELOG.md CHANGED
@@ -7,3 +7,16 @@
7
7
  ## [0.1.1] - 2025-09-16
8
8
 
9
9
  - Actual templating and CSS functionality added
10
+
11
+ ## [0.1.2] - 2025-09-16
12
+
13
+ - Updated README
14
+ - Added main joys.rb
15
+
16
+
17
+ ## [0.1.3] - 2025-09-29
18
+
19
+ - Added configurable markup parser
20
+ - Added Static Site Generator
21
+ - Added Static Data Layer
22
+ - Added CLI Utility
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
@@ -352,17 +295,19 @@ Using simple components and views (only 3 layers of nesting) we can see how Joy
352
295
 
353
296
  #### ❄️ COLD RENDER (new object per call)
354
297
  | | user | system | total | real. |
298
+ |------|--------|--------|--------|--------|
355
299
  |Joys: |0.060535|0.000200|0.060735|0.060736|
356
300
  |Slim: |0.048344|0.000183|0.048527|0.048530|
357
301
  |ERB: |0.301811|0.000808|0.302619|0.302625|
358
302
  |Phlex:|0.069636|0.000470|0.070106|0.071157|
359
303
 
360
304
  #### 🔥 HOT RENDER (cached objects)
361
- | | user | system | total | real |
362
- |Joys: |0.065255| 0.000257|0.065512|0.065512|
363
- |Slim: |0.049323| 0.000295|0.049618|0.049695|
364
- |ERB: |0.309757| 0.001167|0.310924|0.310929|
365
- |Phlex: |0.069663| 0.000141|0.069804|0.069805|
305
+ | | user | system | total | real |
306
+ |------|--------|---------|--------|--------|
307
+ |Joys: |0.065255| 0.000257|0.065512|0.065512|
308
+ |Slim: |0.049323| 0.000295|0.049618|0.049695|
309
+ |ERB: |0.309757| 0.001167|0.310924|0.310929|
310
+ |Phlex:|0.069663| 0.000141|0.069804|0.069805|
366
311
 
367
312
  #### 💾 MEMORY USAGE
368
313
  Joys memory: 532356 bytes
@@ -387,17 +332,19 @@ As template complexity grows, Joys starts to really show off its optimizations.
387
332
 
388
333
  This is the bare bones benchmark, no "cheating" allowed.
389
334
 
390
- | | user | system | total | real |
391
- | Joys (cold)|0.051715|0.000364|0.052079|0.052080|
392
- | ERB (cold)|0.520495|0.003696|0.524191|0.524187|
393
- | Slim (cold)|6.001650|0.019418|6.021068|6.021013|
394
- | Phlex(cold)|0.169567|0.000373|0.169940|0.169938|
335
+ | | user | system | total | real |
336
+ |------|--------|--------|--------|--------|
337
+ | Joys |0.051715|0.000364|0.052079|0.052080|
338
+ | ERB |0.520495|0.003696|0.524191|0.524187|
339
+ | Slim |6.001650|0.019418|6.021068|6.021013|
340
+ | Phlex|0.169567|0.000373|0.169940|0.169938|
395
341
 
396
342
  Note: Joys achieves its 'cold render' speed by eliminating object overhead and using simple string operations with smart memory management.
397
343
 
398
344
  ### 🔥 HOT RENDER (cached objects)
399
345
 
400
346
  | | user | system | total | real |
347
+ |------|---------|--------|---------|--------|
401
348
  |JOYS: | 0.000463|0.000010| 0.000473|0.000473|
402
349
  |SLIM: | 0.045881|0.000358| 0.046239|0.046243|
403
350
  |PHLEX:| 0.167631|0.000760| 0.168391|0.168661|
@@ -815,130 +762,358 @@ Joys is designed to stay out of your way. At its core, it leans functional — y
815
762
  The DSL is the same either way — Joys just adapts.
816
763
 
817
764
 
765
+ ---
766
+
767
+ # Joys Rails Integration
768
+
769
+ ## Installation
770
+
771
+ Add to your Rails app's initializer:
772
+
818
773
  ```ruby
819
- class BaseLayout
820
- include Joys::Helpers
774
+ require 'joys'
775
+ ```
821
776
 
822
- def render(&block)
823
- html {
824
- head { title("My App") }
825
- body { block.call }
826
- }
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])
827
815
  end
828
816
  end
817
+ ```
829
818
 
830
- class Dashboard < BaseLayout
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
+
831
844
  def render
832
- super {
833
- div(cs: "dashboard") {
834
- h1("Dashboard")
835
- p("Rendered through OOP inheritance.")
836
- a("Logout", cs:"btn",href:"/logout")
845
+ data = @data
846
+ Joys.html {
847
+ layout(:main) {
848
+ push(:title) { txt data[:title] }
849
+ push(:body) { _ link_to("About", "/about") }
837
850
  }
838
851
  }
839
852
  end
840
853
  end
841
854
 
842
- puts Dashboard.new.render
843
- # => <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
844
856
  ```
845
857
 
846
- You can pick the style that fits your project, or even mix them.
858
+ ## Rails Helpers
847
859
 
848
- ---
849
- ## 🧩 Bespoke Page Architecture: Content-Driven Flexibility
860
+ All Rails helpers available directly:
850
861
 
851
- **Perfect for landing pages, marketing campaigns, and any layout requiring easy reorganization.**
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
+ ```
852
875
 
853
- Modular Page Architecture lets you build pages as Ruby modules with clean data separation, allowing rapid section reorganization without touching template code.
876
+ ## Context Access
854
877
 
855
- ### The Pattern: Data + Methods + Flexible Assembly
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
856
887
 
857
888
  ```ruby
858
- # pages/product_launch.rb
859
- module ProductLaunch
860
- DATA = {
861
- hero: {
862
- title: "Revolutionary SaaS Platform",
863
- cta: "Start Free Trial"
864
- },
865
- features: {
866
- items: [
867
- { title: "Lightning Fast", desc: "10x faster" },
868
- { title: "Secure", desc: "Bank-level security" }
869
- ]
870
- }
871
- }.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
872
894
 
873
- def self.render
874
- Joys.define(:page, :product_launch) do
875
- layout(:main) {
876
- push(:main) {
877
- raw hero(DATA[:hero])
878
- raw features(DATA[:features])
879
-
880
- # Custom sections between data-driven ones
881
- div(cs: "demo") {
882
- h2("See It In Action")
883
- comp(:demo_video)
884
- }
885
-
886
- comp(:testimonials) # Reusable global component
887
- }
888
- }
889
- end
890
- end
895
+ # Usage in templates
896
+ comp :card, "Hello World"
897
+ ```
891
898
 
892
- private
899
+ ## Page Components
893
900
 
894
- def self.hero(data)
895
- Joys.html {
896
- styles { css ".hero { background: #667eea; padding: 4rem; }" }
897
- # All css rendered and deduplicated within page scope
898
- section(cs: "hero") {
899
- h1(data[:title])
900
- a(data[:cta], href: "/signup", cs: "btn")
901
- }
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
902
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")
903
1075
  end
1076
+ end
1077
+ ```
904
1078
 
905
- def self.features(data)
906
- Joys.html {
907
- section(cs: "features") {
908
- data[:items].each { |item|
909
- div {
910
- h3(item[:title])
911
- p(item[:desc])
912
- }
913
- }
914
- }
915
- }
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 }
916
1089
  end
917
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
+ )
918
1097
  ```
919
1098
 
920
- ### Why This Pattern Works
1099
+ ### Performance Notes
921
1100
 
922
- **📊 Content Management Made Simple**
923
- - All page content in one DATA hash
924
- - Easy updates without touching templates
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
925
1104
 
926
- **🎯 Effortless Reorganization**
927
- - Move sections by reordering method calls
928
- - Client requests become 30-second changes
1105
+ ### Security
929
1106
 
930
- **🧩 Best of All Worlds**
931
- - Data-driven sections for consistency
932
- - Custom HTML where needed
933
- - Reusable global components
934
- - Page-specific anonymous components
1107
+ When processing user-generated content, ensure your markup parser is configured for safe mode:
935
1108
 
936
- **⚡ Performance Benefits**
937
- - No global registry pollution
938
- - Page-scoped CSS compilation
939
- - Leverages existing Joys optimizations
1109
+ ```ruby
1110
+ # With Sparx safe mode
1111
+ Joys::Config.markup_parser = ->(content) {
1112
+ Sparx.parse(content, safe: true)
1113
+ }
1114
+ ```
940
1115
 
941
- This pattern transforms tedious page layout management into an agile, data-driven workflow that scales beautifully.
1116
+ The `?` suffix provides a clean, consistent way to integrate markup processing into your Joys templates while maintaining the framework's exceptional performance characteristics.
942
1117
 
943
1118
  ---
944
1119
 
data/joys-0.1.0.gem ADDED
Binary file
data/joys-0.1.1.gem ADDED
Binary file
data/joys-0.1.2.gem ADDED
Binary file
data/lib/.DS_Store CHANGED
Binary file