rblade 1.0.5 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7334cbd159959066834b8d7407b4b8e0eb8af1aa57501fd3a3ea352b1a043058
4
- data.tar.gz: 104b6b0f7abbf9aee1f35c6ca74ffedc0c771b5d0cfe32796e593ddc55730828
3
+ metadata.gz: 12abc58f2de63aabc65abf34fe60b17b63b8ba70141dbd16447d6d5b42b84a0a
4
+ data.tar.gz: 101e136868a7cbb2d5eb7ab2d117b90013999c1dcd544726fa9a0423a3b6566e
5
5
  SHA512:
6
- metadata.gz: a9956d9b662c3bf264bdd9228087fdaf72ff51972427ae21eb1d33c10358fefcf4e0fcd1a8aa362a6df237d3cee51a3bf8d1831750b0b9e55f6e20709dcefb85
7
- data.tar.gz: 6de71435e6f099b719b907ab9157eb117a6cd8cfd8c6854b657e533525e67d97b5e66b40ccb0aa97c91cc25fd3340897b0db75971383868e4eeaefd21ae3f44a
6
+ metadata.gz: 6a96e14502ccb338afc0779779f8d18901b9f8ea4ef90ba26e0142d24acc7f8a8cac85e290f3109a92b7d82c5752da795481084c733810dc3ffcfed97302a6c4
7
+ data.tar.gz: 0a17982fdb228fb5edaa308e9dbb2c58a16c9733d2bd5bbe89188a4bb1431940558be5904a41d02093fe9ae5346b631a98bcd2dd825fe72e47de390e85912343
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  storage
2
2
  Gemfile.lock
3
+ *.gem
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 1.1.1 [2024-08-23]
2
+ - Remove requirement for including braces in `@props` directive
3
+
4
+ ## 1.1.0 [2024-08-23]
5
+ - Add `@eachWithIndex` directive
6
+ - Fix attribute manager output of attributes with no value (#10)
7
+
1
8
  ## 1.0.5 [2024-08-14]
2
9
  - Allow use of variables in @method directive
3
10
 
data/README.md CHANGED
@@ -222,6 +222,16 @@ In addition to conditional statements, RBlade provides simple directives for wor
222
222
  <p>This is user {{ user.id }}</p>
223
223
  @endEach
224
224
 
225
+ {{-- Compiles to users.each_with_index do |user, index| ... --}}
226
+ @eachWithIndex (user, index in users)
227
+ <p>This is user {{ index }} {{ user.name }}</p>
228
+ @endEachWithIndex
229
+
230
+ {{-- eachWithIndex has a special case for Hashes: in this example, the result is "Item 1: a A" --}}
231
+ @eachWithIndex (key, value, index in {a: 'A'})
232
+ Item #{{ index + 1 }}: {{ key }} {{ value }}
233
+ @endEachWithIndex
234
+
225
235
  @forElse (name in [])
226
236
  <li>{{ name }}</li>
227
237
  @empty
@@ -234,6 +244,12 @@ In addition to conditional statements, RBlade provides simple directives for wor
234
244
  <p>No users</p>
235
245
  @endEachElse
236
246
 
247
+ @eachWithIndexElse (user, index in users)
248
+ <li>{{ index }}: {{ user.name }}</li>
249
+ @empty
250
+ <p>No users</p>
251
+ @endEachWithIndexElse
252
+
237
253
  @while (true)
238
254
  <p>I'm looping forever.</p>
239
255
  @endWhile
@@ -475,7 +491,7 @@ You can define a component's data properties using a `@props` directive at the t
475
491
 
476
492
  ```rblade
477
493
  {{-- alert.rblade --}}
478
- @props({type: "warning", message: required})
494
+ @props(type: "warning", message: required)
479
495
  <div class="{{ type }}">{{ message }}</div>
480
496
  ```
481
497
 
@@ -489,7 +505,7 @@ The `@props` directive accepts a Hash where the key is the name of the attribute
489
505
  All properties in the `@props` directive are automatically removed from `attributes`. Properties with names that aren't valid Ruby variable names or are Ruby reserved keywords are not created as local variables. However, you can reference them via the `attributes` local variable:
490
506
 
491
507
  ```rblade
492
- @props({"for": required, "data-value": nil})
508
+ @props("for": required, "data-value": nil)
493
509
  <div>{{ attributes[:for] }} {{ attributes[:'data-value'] }}</div>
494
510
  ```
495
511
 
@@ -681,7 +697,7 @@ Sometimes a component may need to render multiple different slots in different l
681
697
 
682
698
  ```rblade
683
699
  {{-- /app/views/components/alert.rblade --}}
684
- @props({title: required})
700
+ @props(title: required)
685
701
  <span class="alert-title">{{ title }}</span>
686
702
  <div class="alert alert-danger">
687
703
  {{ slot }}
@@ -736,10 +752,10 @@ Like RBlade components, you can assign additional [attributes](#component-attrib
736
752
  To interact with slot attributes, you can access the `attributes` property of the slot's variable. For more information on how to interact with attributes, please consult the documentation on [component attributes](#component-attributes):
737
753
 
738
754
  ```rblade
739
- @props({
755
+ @props(
740
756
  "heading": required,
741
757
  "footer": required,
742
- })
758
+ )
743
759
 
744
760
  <div {{ attributes.class('border') }}>
745
761
  <h1 {{ heading.attributes.class('text-lg') }}>
@@ -761,7 +777,7 @@ Sometimes, you may wish to return early from a component without printing anythi
761
777
 
762
778
  ```rblade
763
779
  {{-- components/error.rblade --}}
764
- @props({errors: []})
780
+ @props(errors: [])
765
781
  @shouldRender(errors.present?)
766
782
  ...
767
783
  ```
data/REFERENCE.md CHANGED
@@ -30,12 +30,13 @@ By default, RBlade will look for components in the `app/views/components` folder
30
30
  | `<x-name @style({'bg-red-600': is_error})/>` | Conditionally pass styles to a component |
31
31
  | `<x-name attribute/>` | Pass an attribute to a component with value `true` |
32
32
  | `<x-name {{ attributes }}/>` | Pass attributes to a child component |
33
- | `@props({header: "Header"})` | Remove `header` from the attributes Hash and introduce it as a local variable, using the specified value as a default |
34
- | `@props({header: required})` | Remove `header` from the attributes Hash and introduce it as a local variable, raising an error if it is not set |
33
+ | `@props(header: "Header")` | Remove `header` from the attributes Hash and introduce it as a local variable, using the specified value as a default |
34
+ | `@props(header: required)` | Remove `header` from the attributes Hash and introduce it as a local variable, raising an error if it is not set |
35
35
  | `{{ slot }}` | Output the block content passed into the current component |
36
36
  | `<x-name><x-slot::header><h1>Header</h1><//>Content<//>` | Pass a named block to a component |
37
37
  | `{{ header }}` | Output the contents of a named block |
38
38
  | `<div {{ header.attributes }}>` | Output the attributes passed into a named block |
39
+ | `@shouldRender(RUBY_EXPRESSION)` | Only renders the component if RUBY_EXPRESSION evaluates to true |
39
40
 
40
41
  <a name="quick-reference-attributes"></a>
41
42
  ## Attributes
@@ -75,19 +76,22 @@ The attributes variable is an instance of a class that manages attributes. As we
75
76
  <a name="quick-reference-loops"></a>
76
77
  ## Loops
77
78
 
78
- | Syntax | Description |
79
- |:------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------|
80
- | `@while( looping ) ... @endWhile` | Compiles to a Ruby while statement |
81
- | `@until( finished ) ... @endUntil` | Compiles to a Ruby until statement |
82
- | `@for( i in 1..10 ) ... @endFor` | Compiles to a Ruby for loop |
83
- | `@each( i in 1..10 ) ... @endEach` | Calls `each` on the given collection, with `\|i\|` as the block argument |
84
- | `@each( key, value in {a: 1} ) ... @endEach` | Calls `each` on the given Hash, with `\|key, value\|` as the block arguments |
85
- | `@forElse( i in 1..10 ) ... @empty ... @endForElse` | Compiles to a for loop as above, but the block after `@empty` is printed if the given collection is empty |
86
- | `@eachElse( i in 1..10 ) ... @empty ... @endEachElse` | Compiles to a each loop as above, but the block after `@empty` is printed if the given collection is empty |
87
- | `@break` | Break out of the current loop |
88
- | `@next` | Go to the next iteration in the current loop |
89
- | `@break( RUBY_EXPRESSION )` | Break out of the current loop if the expression evaluate to true |
90
- | `@next( RUBY_EXPRESSION )` | Go to the next iteration in the current loop if the expression evaluate to true |
79
+ | Syntax | Description |
80
+ |:-----------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------|
81
+ | `@while( looping ) ... @endWhile` | Compiles to a Ruby while statement |
82
+ | `@until( finished ) ... @endUntil` | Compiles to a Ruby until statement |
83
+ | `@for( i in 1..10 ) ... @endFor` | Compiles to a Ruby for loop |
84
+ | `@each( i in 1..10 ) ... @endEach` | Calls `each` on the given collection, with `\|i\|` as the block argument |
85
+ | `@each( key, value in {a: 1} ) ... @endEach` | Calls `each` on the given Hash, with `\|key, value\|` as the block arguments |
86
+ | `@eachWithIndex( value, index in ['a', 'b'] ) ... @endEachWithIndex` | Calls `each_with_index` on the given Array, with `\|value, index\|` as the block arguments |
87
+ | `@eachWithIndex( key, value, index in {a: 1} ) ... @endEachWithIndex` | Calls `each_with_index` on the given Hash, where `key` is the Hash key, `value` is the Hash value, and `index` is the index |
88
+ | `@forElse( i in 1..10 ) ... @empty ... @endForElse` | Compiles to a for loop as above, but the block after `@empty` is printed if the given collection is empty |
89
+ | `@eachElse( i in 1..10 ) ... @empty ... @endEachElse` | Compiles to a each loop as above, but the block after `@empty` is printed if the given collection is empty |
90
+ | `@eachWithIndexElse( value, index in 1..10 ) ... @empty ... @endEachWithIndexElse` | Compiles to a each_with_index loop as above, but the block after `@empty` is printed if the given collection is empty |
91
+ | `@break` | Break out of the current loop |
92
+ | `@next` | Go to the next iteration in the current loop |
93
+ | `@break( RUBY_EXPRESSION )` | Break out of the current loop if the expression evaluate to true |
94
+ | `@next( RUBY_EXPRESSION )` | Go to the next iteration in the current loop if the expression evaluate to true |
91
95
 
92
96
  <a name="quick-reference-forms"></a>
93
97
  ## Forms
@@ -81,6 +81,8 @@ module RBlade
81
81
  "elsif" => [CompilesConditionals, :compileElsif],
82
82
  "each" => [CompilesLoops, :compileEach],
83
83
  "eachelse" => [CompilesLoops, :compileEachElse],
84
+ "eachwithindex" => [CompilesLoops, :compileEachWithIndex],
85
+ "eachwithindexelse" => [CompilesLoops, :compileEachWithIndexElse],
84
86
  "empty" => [CompilesLoops, :compileEmpty],
85
87
  "empty?" => [CompilesConditionals, :compileEmpty],
86
88
  "end" => [CompilesStatements, :compileEnd],
@@ -42,12 +42,12 @@ module RBlade
42
42
  private
43
43
 
44
44
  def extractProps prop_string
45
- if !prop_string.start_with?("{") || !prop_string.end_with?("}")
46
- raise StandardError.new "Props statement: expecting hash as parameter"
45
+ if prop_string.start_with?("{") && prop_string.end_with?("}")
46
+ prop_string = prop_string.delete_prefix("{").delete_suffix("}")
47
47
  end
48
48
 
49
49
  props = {}
50
- prop_strings = Tokenizer.extractCommaSeparatedValues prop_string[1..-2]
50
+ prop_strings = Tokenizer.extractCommaSeparatedValues prop_string
51
51
 
52
52
  prop_strings.each do |prop|
53
53
  prop.strip!
@@ -19,27 +19,74 @@ module RBlade
19
19
 
20
20
  def compileEach args
21
21
  if args.nil? || args.count > 2
22
- raise StandardError.new "Each statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
22
+ raise StandardError.new "Each statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
23
23
  end
24
- # Allow variables to be a key, value pair
25
- args = args.join ","
24
+ last_arg, collection = args.pop.split(" in ")
25
+ args.push(last_arg)
26
26
 
27
- variables, collection = args.split(" in ")
27
+ if collection.nil?
28
+ raise StandardError.new "Each statement: collection not found (expecting 'in')"
29
+ end
28
30
 
29
- "#{collection}.each do |#{variables}|;"
31
+ "#{collection}.each do |#{args.join(",")}|;"
32
+ end
33
+
34
+ def compileEachWithIndex args
35
+ if args.nil? || args.count > 3
36
+ raise StandardError.new "Each with index statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 to 3)"
37
+ end
38
+ last_arg, collection = args.pop.split(" in ")
39
+ args.push(last_arg)
40
+
41
+ if collection.nil?
42
+ raise StandardError.new "Each with index statement: collection not found (expecting 'in')"
43
+ end
44
+
45
+ # Special case for 3 arguments: the first 2 arguments are the key/value pair in a Hash, and
46
+ # the third is the index
47
+ if args.count == 3
48
+ "#{collection}.each_with_index do |_ivar,#{args[2]}|;#{args[0]},#{args[1]}=_ivar;"
49
+ else
50
+ "#{collection}.each_with_index do |#{args.join(",")}|;"
51
+ end
30
52
  end
31
53
 
32
54
  def compileEachElse args
33
55
  if args.nil? || args.count > 2
34
56
  raise StandardError.new "Each else statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
35
57
  end
36
- # Allow variables to be a key, value pair
37
- args = args.join ","
58
+ last_arg, collection = args.pop.split(" in ")
59
+ args.push(last_arg)
60
+
61
+ if collection.nil?
62
+ raise StandardError.new "Each else statement: collection not found (expecting 'in')"
63
+ end
64
+
38
65
  @loop_else_counter += 1
39
66
 
40
- variables, collection = args.split(" in ")
67
+ "_looped_#{@loop_else_counter}=false;#{collection}.each do |#{args.join(",")}|;_looped_#{@loop_else_counter}=true;"
68
+ end
69
+
70
+ def compileEachWithIndexElse args
71
+ if args.nil? || args.count > 3
72
+ raise StandardError.new "Each with index statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 to 3)"
73
+ end
74
+ last_arg, collection = args.pop.split(" in ")
75
+ args.push(last_arg)
76
+
77
+ if collection.nil?
78
+ raise StandardError.new "Each with index statement: collection not found (expecting 'in')"
79
+ end
80
+
81
+ @loop_else_counter += 1
41
82
 
42
- "_looped_#{@loop_else_counter}=false;#{collection}.each do |#{variables}|;_looped_#{@loop_else_counter}=true;"
83
+ # Special case for 3 arguments: the first 2 arguments are the key/value pair in a Hash, and
84
+ # the third is the index
85
+ if args.count == 3
86
+ "_looped_#{@loop_else_counter}=false;#{collection}.each_with_index do |_ivar,#{args[2]}|;#{args[0]},#{args[1]}=_ivar;_looped_#{@loop_else_counter}=true;"
87
+ else
88
+ "_looped_#{@loop_else_counter}=false;#{collection}.each_with_index do |#{args.join(",")}|;_looped_#{@loop_else_counter}=true;"
89
+ end
43
90
  end
44
91
 
45
92
  def compileFor args
@@ -37,7 +37,7 @@ module RBlade
37
37
  attributes ||= @attributes
38
38
 
39
39
  attributes.map do |key, value|
40
- "#{key}=\"#{(value == true) ? key : h(value)}\""
40
+ (value == true) ? key : "#{key}=\"#{h(value)}\""
41
41
  end.join " "
42
42
  end
43
43
 
data/rblade.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rblade"
3
- s.version = "1.0.5"
3
+ s.version = "1.1.1"
4
4
  s.summary = "A component-first templating engine for Rails"
5
5
  s.description = "RBlade is a simple, yet powerful templating engine for Ruby on Rails, inspired by Laravel Blade."
6
6
  s.authors = ["Simon J"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rblade
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon J
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-14 00:00:00.000000000 Z
11
+ date: 2024-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -140,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  requirements: []
143
- rubygems_version: 3.0.3.1
143
+ rubygems_version: 3.3.5
144
144
  signing_key:
145
145
  specification_version: 4
146
146
  summary: A component-first templating engine for Rails