theo-rails 0.2.0 → 0.3.1

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/theo-rails/theo.rb +59 -27
  3. metadata +17 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f522cd4a9045802867fb16443c798ed1cb1da0a07b20dfa00fe4c9b1f58c616
4
- data.tar.gz: 8373f7e46efb666a7cbd07b15f51dd5001375131b2f8fec3bcdd7adb0957c812
3
+ metadata.gz: f2b210d661f6fd028694497e83aa3b205c5fd89a6d3136fbc1faca2f4e4ddb29
4
+ data.tar.gz: 734ea63e9962dac3a87a207b0cb82d165c0f4d54a6d791b5398c46dfda15cbc4
5
5
  SHA512:
6
- metadata.gz: '081b4756de992bdfc8fd63a17f6c593d639b2c586bd5a3bfc9cc8555757fac559d59f24e81142edd2b55333f07e1f22ef10147c3fc16b6dc1d53e30f427a7fbc'
7
- data.tar.gz: 7221da0b1ef26ef372d5765a776bc89aa1af725b4595b02814b7832476edc05ee96ddde16d2877f9d8ffb16929f912bb82c7bf9014fc5efa56b4f9af541fc9d7
6
+ metadata.gz: 85d866b6839ed5736b50dd5026b65e925f9dae25de88e24d47a9856e7454820fa9f7e8e855a022534ee636c2dfbc0b78584eb16f31543d97e327c1ff14a63b92
7
+ data.tar.gz: 0b21d4fddb3b4a3618117d23fc2e846881ffc2f7e2b5667187ff33d220cea790ea18c6728a7d2167e228cfea83bed0d7dcab86210eb9cfbf4365ab25a2c8692d
@@ -1,49 +1,81 @@
1
1
  module Theo
2
2
  module Rails
3
- ATTRIBUTE_NAME = /[\w\-:@]+/
3
+ ATTRIBUTE_NAME = /(?<name>[a-z][\w\-:@]*)/
4
4
  ATTRIBUTE_VALUE = /(?:(?:"(?<value>[^"]*)")|(?:'(?<value>[^']*)'))/
5
- ATTRIBUTE = /(?:(?:(?<name>#{ATTRIBUTE_NAME.source})\s*=\s*#{ATTRIBUTE_VALUE.source})|(?<name>#{ATTRIBUTE_NAME.source}))/
6
- DYNAMIC_ATTRIBUTE = /(?:(?<name>#{ATTRIBUTE_NAME.source})\s*%=\s*#{ATTRIBUTE_VALUE.source})/
5
+ ATTRIBUTE = /(?:(?:#{ATTRIBUTE_NAME.source}\s*=\s*#{ATTRIBUTE_VALUE.source})|#{ATTRIBUTE_NAME.source})/
6
+ DYNAMIC_ATTRIBUTE = /(?:(?:#{ATTRIBUTE_NAME.source}\s*%=\s*#{ATTRIBUTE_VALUE.source})|(?:#{ATTRIBUTE_NAME.source}%))/
7
+ RESERVED_ATTRIBUTE_NAME = %w[alias and begin break case class def do else elsif end ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield].to_set
7
8
  ATTRIBUTES = /(?<attrs>(?:\s+#{ATTRIBUTE.source})*)/
8
- LITERAL_ATTRIBUTES = %i[path as yields].freeze
9
- PARTIAL_TAG = /(?<partial>_[\w-]+)/
10
- PARTIAL = /(?:<#{PARTIAL_TAG.source}#{ATTRIBUTES.source}\s*>(?<content>.*?)<\/\k<partial>>)|(?:<#{PARTIAL_TAG.source}#{ATTRIBUTES.source}\s*\/>)/im
9
+ LITERAL_ATTRIBUTES = %i[path as yields collection].freeze
10
+ PARTIAL_TAG = /(?<partial>_\w+)/
11
+ PARTIAL = /(?:<#{PARTIAL_TAG.source}#{ATTRIBUTES.source}\s*>(?<content>.*?)<\/\k<partial>>)|(?:<#{PARTIAL_TAG.source}#{ATTRIBUTES.source}\s*\/>)/
12
+ COMPONENT_TAG = /(?<component>[A-Z]\w+)/
13
+ COMPONENT = /(?:<#{COMPONENT_TAG.source}#{ATTRIBUTES.source}\s*>(?<content>.*?)<\/\k<component>>)|(?:<#{COMPONENT_TAG.source}#{ATTRIBUTES.source}\s*\/>)/
14
+ TEMPLATE = /(?:#{PARTIAL.source})|(?:#{COMPONENT.source})/m
11
15
  DYNAMIC_EXPRESSION = /^<%=([^%]*)%>$/
12
16
 
13
17
  class Theo
14
18
  def process(source)
15
19
  # Attributes
16
- source = source.gsub(DYNAMIC_ATTRIBUTE, '\k<name>="<%=\k<value>%>"')
20
+ source = source.gsub(DYNAMIC_ATTRIBUTE) do |_|
21
+ match = Regexp.last_match
22
+
23
+ name = match[:name]
24
+
25
+ # See https://island94.org/2024/06/rails-strict-locals-local_assigns-and-reserved-keywords for more info
26
+ value = match[:value] || (RESERVED_ATTRIBUTE_NAME.include?(name) ? "binding.local_variable_get('#{name}')" : name)
27
+
28
+ "#{name}=\"<%= #{value} %>\""
29
+ end
17
30
 
18
31
  # Partials
19
- source.gsub(PARTIAL) do |_|
32
+ source.gsub(TEMPLATE) do |_|
20
33
  match = Regexp.last_match
21
- partial = (match[:partial]).delete_prefix('_')
34
+
35
+ partial = match[:partial]
36
+ component = match[:component]
37
+
22
38
  attributes = match[:attrs] || ''
23
- content = match[:content]&.strip
39
+ content = match[:content]
24
40
 
25
41
  attributes = process_attributes(attributes)
26
42
 
27
- partial = "#{attributes.delete(:path)}/#{partial}" if attributes[:path]
43
+ path = attributes.delete(:path)
28
44
 
29
- collection = ''
30
- if attributes[:collection]
31
- collection = attributes.delete(:collection)
45
+ collection = attributes.delete(:collection)
46
+ as = attributes.delete(:as)
32
47
 
33
- as = ''
34
- if attributes[:as]
35
- as = attributes.delete(:as)
36
- as = ", as: '#{as}'"
37
- end
38
- collection = ", collection: #{collection}#{as}"
39
- end
48
+ yields = attributes.delete(:yields)
49
+ yields = " |#{yields}|" if yields
50
+
51
+ locals = attributes.empty? ? '' : attributes.map { |k, v| "'#{k}': #{v}" }.join(', ')
52
+
53
+ if partial
54
+ partial = partial.delete_prefix('_')
40
55
 
41
- yields = "|#{attributes.delete(:yields)}|" if attributes[:yields]
56
+ partial = "#{path}/#{partial}" if path
42
57
 
43
- if content
44
- output = "<%= render '#{partial}', {#{attributes.map {|k,v| "'#{k}': #{v}"}.join(', ')}} do #{yields || ''} %>#{process(content)}<% end %>"
58
+ as = as ? ", as: '#{as}'" : ''
59
+ collection = ", collection: #{collection}#{as}" if collection
60
+
61
+ if content
62
+ locals = ", {#{locals}}" unless locals.empty?
63
+ output = "<%= render '#{partial}'#{locals} do#{yields} %>#{process(content)}<% end %>"
64
+ else
65
+ locals = ", locals: {#{locals}}" unless locals.empty?
66
+ output = "<%= render partial: '#{partial}'#{collection}#{locals} %>"
67
+ end
45
68
  else
46
- output = "<%= render partial: '#{partial}'#{collection}, locals: {#{attributes.map {|k,v| "'#{k}': #{v}"}.join(', ')}} %>"
69
+ component = "#{component}Component"
70
+
71
+ if content
72
+ output = "<%= render #{component}.new(#{locals}) do#{yields} %>#{process(content)}<% end %>"
73
+ elsif collection
74
+ locals = ", #{locals}" unless locals.empty?
75
+ output = "<%= render #{component}.with_collection(#{collection}#{locals}) %>"
76
+ else
77
+ output = "<%= render #{component}.new(#{locals}) %>"
78
+ end
47
79
  end
48
80
 
49
81
  output
@@ -57,7 +89,7 @@ module Theo
57
89
  .map do |attr|
58
90
  name = attr[:name].to_sym
59
91
  value = attr[:value]
60
- value = attribute(value) if LITERAL_ATTRIBUTES.exclude?(name)
92
+ value = attribute(value) unless LITERAL_ATTRIBUTES.include?(name)
61
93
  [name, value]
62
94
  end
63
95
  .to_h
@@ -67,7 +99,7 @@ module Theo
67
99
  # TODO: support attributes like "a<%= b %>c
68
100
 
69
101
  match = DYNAMIC_EXPRESSION.match(source)
70
- return match[1] if match
102
+ return match[1].strip if match
71
103
 
72
104
  "'#{source}'"
73
105
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theo-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jarek Lipski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-13 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3'
13
27
  description: HTML-like template language for Rails with natural partial syntax
14
28
  email: jarek@jareklipski.com
15
29
  executables: []