rbexy 1.1.0 → 2.0.0.beta1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +18 -0
  4. data/lib/rbexy.rb +26 -9
  5. data/lib/rbexy/ast_transformer.rb +21 -0
  6. data/lib/rbexy/component.rb +1 -6
  7. data/lib/rbexy/component_resolver.rb +60 -0
  8. data/lib/rbexy/configuration.rb +18 -1
  9. data/lib/rbexy/lexer.rb +20 -13
  10. data/lib/rbexy/nodes.rb +15 -135
  11. data/lib/rbexy/nodes/abstract_attr.rb +12 -0
  12. data/lib/rbexy/nodes/abstract_element.rb +13 -0
  13. data/lib/rbexy/nodes/abstract_node.rb +58 -0
  14. data/lib/rbexy/nodes/component_element.rb +69 -0
  15. data/lib/rbexy/nodes/component_prop.rb +29 -0
  16. data/lib/rbexy/nodes/declaration.rb +15 -0
  17. data/lib/rbexy/nodes/expression.rb +15 -0
  18. data/lib/rbexy/nodes/expression_group.rb +55 -0
  19. data/lib/rbexy/nodes/html_attr.rb +13 -0
  20. data/lib/rbexy/nodes/html_element.rb +48 -0
  21. data/lib/rbexy/nodes/newline.rb +9 -0
  22. data/lib/rbexy/nodes/raw.rb +23 -0
  23. data/lib/rbexy/nodes/root.rb +19 -0
  24. data/lib/rbexy/nodes/text.rb +15 -0
  25. data/lib/rbexy/nodes/util.rb +9 -0
  26. data/lib/rbexy/parser.rb +22 -16
  27. data/lib/rbexy/rails/component_template_resolver.rb +3 -3
  28. data/lib/rbexy/rails/controller_helper.rb +0 -6
  29. data/lib/rbexy/rails/engine.rb +1 -7
  30. data/lib/rbexy/refinements.rb +5 -0
  31. data/lib/rbexy/refinements/array.rb +9 -0
  32. data/lib/rbexy/refinements/array/find_map.rb +13 -0
  33. data/lib/rbexy/refinements/array/insert_between_types.rb +26 -0
  34. data/lib/rbexy/refinements/array/map_type_when_neighboring_type.rb +26 -0
  35. data/lib/rbexy/runtime.rb +15 -23
  36. data/lib/rbexy/template.rb +12 -0
  37. data/lib/rbexy/version.rb +1 -1
  38. data/rbexy.gemspec +1 -0
  39. metadata +41 -11
  40. data/example.rb +0 -113
  41. data/lib/rbexy/component_providers/namespaced_rbexy_provider.rb +0 -20
  42. data/lib/rbexy/component_providers/rbexy_provider.rb +0 -21
  43. data/lib/rbexy/component_providers/view_component_provider.rb +0 -21
  44. data/lib/rbexy/component_tag_builder.rb +0 -19
  45. data/lib/rbexy/hash_mash.rb +0 -15
  46. data/lib/rbexy/view_context_helper.rb +0 -19
@@ -1,8 +1,8 @@
1
- require "action_view"
2
-
3
1
  module Rbexy
4
2
  module Rails
5
3
  class ComponentTemplateResolver < ActionView::FileSystemResolver
4
+ VIRTUAL_ROOT = "rbexy_component".freeze
5
+
6
6
  # Rails 6 requires us to override `_find_all` in order to hook
7
7
  def _find_all(name, prefix, partial, details, key, locals)
8
8
  find_templates(name, prefix, partial, details, locals)
@@ -19,7 +19,7 @@ module Rbexy
19
19
  Dir["#{templates_path}.*{#{extensions}}"].map do |template_path|
20
20
  source = File.binread(template_path)
21
21
  handler = ActionView::Template.handler_for_extension(File.extname(template_path)[1..-1])
22
- virtual_path = ["rbexy_component", prefix, name].join("/")
22
+ virtual_path = File.join(VIRTUAL_ROOT, prefix, name)
23
23
 
24
24
  ActionView::Template.new(
25
25
  source,
@@ -1,16 +1,10 @@
1
- require "active_support/concern"
2
-
3
1
  module Rbexy
4
2
  module Rails
5
3
  module ControllerHelper
6
4
  extend ActiveSupport::Concern
7
5
  include ComponentContext
8
6
 
9
- def rbexy_component_provider; end
10
-
11
7
  included do
12
- helper ViewContextHelper
13
- helper_method :rbexy_component_provider
14
8
  helper_method :rbexy_context, :create_context, :use_context
15
9
  end
16
10
 
@@ -4,7 +4,7 @@ module Rbexy
4
4
  module Rails
5
5
  class Engine < ::Rails::Engine
6
6
  initializer "rbexy" do |app|
7
- template_handler = proc { |template, source| Rbexy.compile(source) }
7
+ template_handler = proc { |template, source| Rbexy.compile(template) }
8
8
 
9
9
  ActionView::Template.register_template_handler(:rbx, template_handler)
10
10
 
@@ -12,13 +12,7 @@ module Rbexy
12
12
  include ControllerHelper
13
13
  end
14
14
 
15
- if defined?(ViewComponent)
16
- ViewComponent::Base.include ViewContextHelper
17
- end
18
-
19
15
  Rbexy.configure do |config|
20
- require "rbexy/component_providers/rbexy_provider"
21
- config.component_provider = ComponentProviders::RbexyProvider.new
22
16
  config.template_paths << ::Rails.root.join("app", "components")
23
17
  config.enable_context = true
24
18
  end
@@ -0,0 +1,5 @@
1
+ module Rbexy
2
+ module Refinements
3
+ autoload :Array, "rbexy/refinements/array"
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module Rbexy
2
+ module Refinements
3
+ module Array
4
+ autoload :FindMap, "rbexy/refinements/array/find_map"
5
+ autoload :InsertBetweenTypes, "rbexy/refinements/array/insert_between_types"
6
+ autoload :MapTypeWhenNeighboringType, "rbexy/refinements/array/map_type_when_neighboring_type"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Rbexy
2
+ module Refinements
3
+ module Array
4
+ module FindMap
5
+ refine ::Array do
6
+ def find_map
7
+ lazy.map { |i| yield(i) }.reject { |v| !v }.first
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module Rbexy
2
+ module Refinements
3
+ module Array
4
+ module InsertBetweenTypes
5
+ refine ::Array do
6
+ def insert_between_types(type1, type2, &block)
7
+ map.with_index do |curr, i|
8
+ prev_i = i - 1
9
+
10
+ if prev_i >= 0 && InsertBetweenTypes.one_of_each_type?([self[prev_i], curr], [type1, type2])
11
+ [block.call, curr]
12
+ else
13
+ [curr]
14
+ end
15
+ end.flatten
16
+ end
17
+ end
18
+
19
+ def self.one_of_each_type?(items_pair, types_pair)
20
+ items_pair[0].is_a?(types_pair[0]) && items_pair[1].is_a?(types_pair[1]) ||
21
+ items_pair[0].is_a?(types_pair[1]) && items_pair[1].is_a?(types_pair[0])
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Rbexy
2
+ module Refinements
3
+ module Array
4
+ module MapTypeWhenNeighboringType
5
+ refine ::Array do
6
+ def map_type_when_neighboring_type(map_type, neighboring_type, &block)
7
+ map.with_index do |curr, i|
8
+ prev_i = i - 1
9
+ next_i = i + 1
10
+
11
+ if !curr.is_a?(map_type)
12
+ curr
13
+ elsif prev_i >= 0 && self[prev_i].is_a?(neighboring_type)
14
+ block.call(curr)
15
+ elsif next_i < length && self[next_i].is_a?(neighboring_type)
16
+ block.call(curr)
17
+ else
18
+ curr
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,37 +1,29 @@
1
- require "active_support/all"
2
- require "action_view/helpers"
3
- require "action_view/context"
4
- require "action_view/buffers"
5
-
6
1
  module Rbexy
7
2
  class Runtime
8
3
  include ActionView::Context
9
4
  include ActionView::Helpers::TagHelper
10
- include ViewContextHelper
11
5
  include ComponentContext
12
6
 
13
- DefaultTagBuilder = ActionView::Helpers::TagHelper::TagBuilder
14
-
15
- def self.create_tag_builder(context, provider = nil)
16
- provider = provider ||
17
- provider_from_context(context) ||
18
- Rbexy.configuration.component_provider
7
+ def self.tag_builder
8
+ # TagBuilder requires a view_context arg, but it's only used in #tag_string.
9
+ # Since all we need is #tag_options, we pass in a nil view_context.
10
+ @tag_builder ||= TagBuilder.new(nil)
11
+ end
19
12
 
20
- if provider
21
- ComponentTagBuilder.new(context, provider)
22
- else
23
- ActionView::Helpers::TagHelper::TagBuilder.new(context)
24
- end
13
+ def self.splat_attrs(attrs_hash)
14
+ tag_builder.tag_options(attrs_hash).html_safe
25
15
  end
26
16
 
27
- def self.provider_from_context(context)
28
- if context.respond_to?(:rbexy_component_provider)
29
- context.rbexy_component_provider
30
- end
17
+ def self.expr_out(*value)
18
+ return if value.length == 0
19
+ value = value.first
20
+
21
+ value = html_safe_array?(value) ? value.join.html_safe : value
22
+ [nil, false].include?(value) ? "" : value.to_s
31
23
  end
32
24
 
33
- def initialize(component_provider = nil)
34
- @rbexy_tag = self.class.create_tag_builder(self, component_provider)
25
+ def self.html_safe_array?(value)
26
+ value.is_a?(Array) && value.all? { |v| v.respond_to?(:html_safe?) && v.html_safe? }
35
27
  end
36
28
 
37
29
  def evaluate(code)
@@ -0,0 +1,12 @@
1
+ module Rbexy
2
+ class Template
3
+ attr_reader :source, :identifier
4
+
5
+ Anonymous = Class.new(String).new.freeze
6
+
7
+ def initialize(source, identifier = Anonymous)
8
+ @source = source
9
+ @identifier = identifier
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Rbexy
2
- VERSION = "1.1.0"
2
+ VERSION = "2.0.0.beta1"
3
3
  end
@@ -36,4 +36,5 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency "rake"
37
37
  spec.add_development_dependency "pry-byebug"
38
38
  spec.add_development_dependency "sqlite3"
39
+ spec.add_development_dependency "memory_profiler", "~> 0.9.14"
39
40
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbexy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Giancola
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-20 00:00:00.000000000 Z
11
+ date: 2021-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -180,6 +180,20 @@ dependencies:
180
180
  - - ">="
181
181
  - !ruby/object:Gem::Version
182
182
  version: '0'
183
+ - !ruby/object:Gem::Dependency
184
+ name: memory_profiler
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: 0.9.14
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - "~>"
195
+ - !ruby/object:Gem::Version
196
+ version: 0.9.14
183
197
  description:
184
198
  email:
185
199
  - patbenatar@gmail.com
@@ -201,27 +215,43 @@ files:
201
215
  - bin/console
202
216
  - bin/setup
203
217
  - docker-compose.yml
204
- - example.rb
205
218
  - lib/rbexy.rb
219
+ - lib/rbexy/ast_transformer.rb
206
220
  - lib/rbexy/component.rb
207
221
  - lib/rbexy/component/backtrace_cleaner.rb
208
222
  - lib/rbexy/component_context.rb
209
- - lib/rbexy/component_providers/namespaced_rbexy_provider.rb
210
- - lib/rbexy/component_providers/rbexy_provider.rb
211
- - lib/rbexy/component_providers/view_component_provider.rb
212
- - lib/rbexy/component_tag_builder.rb
223
+ - lib/rbexy/component_resolver.rb
213
224
  - lib/rbexy/configuration.rb
214
- - lib/rbexy/hash_mash.rb
215
225
  - lib/rbexy/lexer.rb
216
226
  - lib/rbexy/nodes.rb
227
+ - lib/rbexy/nodes/abstract_attr.rb
228
+ - lib/rbexy/nodes/abstract_element.rb
229
+ - lib/rbexy/nodes/abstract_node.rb
230
+ - lib/rbexy/nodes/component_element.rb
231
+ - lib/rbexy/nodes/component_prop.rb
232
+ - lib/rbexy/nodes/declaration.rb
233
+ - lib/rbexy/nodes/expression.rb
234
+ - lib/rbexy/nodes/expression_group.rb
235
+ - lib/rbexy/nodes/html_attr.rb
236
+ - lib/rbexy/nodes/html_element.rb
237
+ - lib/rbexy/nodes/newline.rb
238
+ - lib/rbexy/nodes/raw.rb
239
+ - lib/rbexy/nodes/root.rb
240
+ - lib/rbexy/nodes/text.rb
241
+ - lib/rbexy/nodes/util.rb
217
242
  - lib/rbexy/parser.rb
218
243
  - lib/rbexy/rails.rb
219
244
  - lib/rbexy/rails/component_template_resolver.rb
220
245
  - lib/rbexy/rails/controller_helper.rb
221
246
  - lib/rbexy/rails/engine.rb
247
+ - lib/rbexy/refinements.rb
248
+ - lib/rbexy/refinements/array.rb
249
+ - lib/rbexy/refinements/array/find_map.rb
250
+ - lib/rbexy/refinements/array/insert_between_types.rb
251
+ - lib/rbexy/refinements/array/map_type_when_neighboring_type.rb
222
252
  - lib/rbexy/runtime.rb
253
+ - lib/rbexy/template.rb
223
254
  - lib/rbexy/version.rb
224
- - lib/rbexy/view_context_helper.rb
225
255
  - rbexy.gemspec
226
256
  homepage: https://github.com/patbenatar/rbexy
227
257
  licenses:
@@ -241,9 +271,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
241
271
  version: 2.3.0
242
272
  required_rubygems_version: !ruby/object:Gem::Requirement
243
273
  requirements:
244
- - - ">="
274
+ - - ">"
245
275
  - !ruby/object:Gem::Version
246
- version: '0'
276
+ version: 1.3.1
247
277
  requirements: []
248
278
  rubygems_version: 3.1.2
249
279
  signing_key:
data/example.rb DELETED
@@ -1,113 +0,0 @@
1
- require "bundler"
2
- Bundler.require
3
-
4
- require "active_support/all"
5
- require "action_view/helpers"
6
- require "action_view/context"
7
- require "action_view/buffers"
8
-
9
- template_string = <<-RBX
10
- # A comment here
11
- <div>
12
- <h1 {**{ class: "myClass" }} {**splat_attrs}>Hello world</h1>
13
- <div {**{ class: "myClass" }}></div>
14
- Some words
15
- # A comment here
16
- # A comment here
17
- <p>Lorem ipsum</p>
18
- <input type="submit" value={@ivar_val} disabled />
19
- {true && <p>Is true</p>}
20
- {false && <p>Is false</p>}
21
- {true ? <p {**{ class: "myClass" }}>Ternary is {'true'.upcase}</p> : <p>Ternary is false</p>}
22
- <Button prop1="val1" prop2={true && "val2"} multi-word-prop="value">the content</Button>
23
- <Forms.TextField label={->(n) { <label id={n}>Something</label> }} note={<p>the note</p>} />
24
- <ul>
25
- # A comment here
26
- {["hi", "there", "nick"].map { |val| <li>{val}</li> }}
27
- </ul>
28
- <p
29
- class="something">Text</p>
30
- <input
31
- class="foobar"
32
- />
33
- <div
34
- with="lots"
35
- of="attributes"
36
- >
37
- Content
38
- </div>
39
- # comment
40
- </div>
41
- # comment
42
- RBX
43
-
44
- module Components
45
- class ButtonComponent
46
- def initialize(prop1:, prop2:, multi_word_prop:)
47
- @prop1 = prop1
48
- @prop2 = prop2
49
- @multi_word_prop = multi_word_prop
50
- end
51
-
52
- def render
53
- # Render it yourself, call one of Rails view helpers (link_to,
54
- # content_tag, etc), or use a template file. Be sure to render
55
- # children by yielding to the given block.
56
- "<button class=\"#{[@prop1, @prop2, @multi_word_prop].join("-")}\">#{yield}</button>"
57
- end
58
- end
59
-
60
- module Forms
61
- class TextFieldComponent
62
- def initialize(label:, note:, **attrs)
63
- @label = label
64
- @note = note
65
- end
66
-
67
- def render
68
- "#{@label.call(2)} <input type=\"text\" />#{@note}"
69
- end
70
- end
71
- end
72
- end
73
-
74
- class ComponentProvider
75
- def match?(name)
76
- find(name) != nil
77
- end
78
-
79
- def render(context, name, **attrs, &block)
80
- props = attrs.transform_keys { |k| ActiveSupport::Inflector.underscore(k.to_s).to_sym }
81
- find(name).new(**props).render(&block)
82
- end
83
-
84
- private
85
-
86
- def find(name)
87
- ActiveSupport::Inflector.constantize("Components::#{name}Component")
88
- rescue NameError => e
89
- raise e unless e.message =~ /constant/
90
- nil
91
- end
92
- end
93
-
94
- class MyRuntime < Rbexy::Runtime
95
- def initialize(component_provider)
96
- super(component_provider)
97
- @ivar_val = "ivar value"
98
- end
99
-
100
- def splat_attrs
101
- {
102
- key1: "val1",
103
- key2: "val2"
104
- }
105
- end
106
- end
107
-
108
- puts "=============== Compiled ruby code ==============="
109
- code = Rbexy.compile(template_string)
110
- puts code
111
-
112
- puts "=============== Result of eval ==============="
113
- puts MyRuntime.new(ComponentProvider.new).evaluate(code)
@@ -1,20 +0,0 @@
1
- module Rbexy
2
- module ComponentProviders
3
- class NamespacedRbexyProvider < Rbexy::ComponentProviders::RbexyProvider
4
- attr_reader :namespaces
5
-
6
- def initialize(*namespaces)
7
- @namespaces = namespaces
8
- end
9
-
10
- def find(name)
11
- namespaces.each do |namespace|
12
- result = super("#{namespace}::#{name}")
13
- return result if result != nil
14
- end
15
-
16
- super
17
- end
18
- end
19
- end
20
- end