heartml 1.0.0.beta6 → 1.0.0.beta8

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: c09bc70bc5761750cc99010a62b106ff435e0f0ee9f72a259d62631c8913785c
4
- data.tar.gz: 44bb47fca2eb847d2e6636ae99eb8446d32b488702a6c8e5dc57d39d571bf076
3
+ metadata.gz: 9af36d404ea8e79e505dd4ae52f7154d697bccf0722bec8b35a34172a7620d2f
4
+ data.tar.gz: 7870f15ce64e811f91ccb07c5b20fe5a010af2d2c4eac355b2583ac8e859730c
5
5
  SHA512:
6
- metadata.gz: 3590d652c24107deb627e3f9aa0114411c3077118b38ad54fdf986019ad5b7a4262682ff407b575796085f4a54c2331a7c82a6a95b6941484242ddc838aa17f8
7
- data.tar.gz: 4b302508a049f09c1009d126a41f73220e63a3ffe9647f9acb689de3df5e3df0977a05fce48b62683c0e139dcc0ef2136d27ecafe3ca77bcc4cf87a3565b1c2f
6
+ metadata.gz: df9a6f3028cccad882c443af444734f969a3be9fce3aad110114c5728692db08a7678645aecac64858ab0454041c7142387b86fe02813a264dafcd77828579e3
7
+ data.tar.gz: 152c74f825813fe9183fe37f6cb491670804b3ecc53ec5277a0f4a5161ddbb199d4f332333be417d19e59ca99b8d69353dad9270f4370c9814a6c54720fe879c
data/.rubocop.yml CHANGED
@@ -3,7 +3,7 @@ require:
3
3
  - rubocop-rake
4
4
 
5
5
  AllCops:
6
- TargetRubyVersion: 3.0
6
+ TargetRubyVersion: 3.1
7
7
  NewCops: enable
8
8
 
9
9
  Metrics/MethodLength:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
+ # Changelog
2
+
1
3
  ## [Unreleased]
2
4
 
5
+ ## [1.0.0.beta8] - 2023-09-25
6
+
7
+ - Simplify modules structure, add tag swap feature, other refactors
8
+ - Bump min Ruby version to 3.1
9
+
10
+ ## [1.0.0.beta7] - 2023-09-25
11
+
12
+ - Add Railtie for Heartml component support in Rails
13
+
3
14
  ## [1.0.0.beta6] - 2023-09-06
4
15
 
5
16
  - Improvements to directives syntax (use lambda argument instead of block)
data/Gemfile CHANGED
@@ -14,4 +14,5 @@ gem "rubocop", "~> 1.21"
14
14
  gem "rubocop-minitest", "~> 0.22.1"
15
15
  gem "rubocop-rake", "~> 0.6.0"
16
16
 
17
+ gem "hash_with_dot_access", "~> 1.2"
17
18
  gem "solargraph", "~> 0.47.2", group: :development
data/Gemfile.lock CHANGED
@@ -1,73 +1,86 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- heartml (1.0.0.beta6)
4
+ heartml (1.0.0.beta8)
5
5
  concurrent-ruby (~> 1.2)
6
6
  nokolexbor (>= 0.4.2)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activesupport (7.0.4.3)
11
+ activesupport (7.1.3)
12
+ base64
13
+ bigdecimal
12
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ connection_pool (>= 2.2.5)
16
+ drb
13
17
  i18n (>= 1.6, < 2)
14
18
  minitest (>= 5.1)
19
+ mutex_m
15
20
  tzinfo (~> 2.0)
16
21
  ast (2.4.2)
17
22
  backport (1.2.0)
18
- benchmark (0.2.1)
19
- concurrent-ruby (1.2.2)
20
- diff-lcs (1.5.0)
23
+ base64 (0.2.0)
24
+ benchmark (0.3.0)
25
+ bigdecimal (3.1.6)
26
+ concurrent-ruby (1.2.3)
27
+ connection_pool (2.4.1)
28
+ diff-lcs (1.5.1)
29
+ drb (2.2.0)
30
+ ruby2_keywords
21
31
  e2mmap (0.1.0)
22
32
  hash_with_dot_access (1.2.0)
23
33
  activesupport (>= 5.0.0, < 8.0)
24
- i18n (1.12.0)
34
+ i18n (1.14.1)
25
35
  concurrent-ruby (~> 1.0)
26
- jaro_winkler (1.5.4)
27
- json (2.6.3)
36
+ jaro_winkler (1.5.6)
37
+ json (2.7.1)
28
38
  kramdown (2.4.0)
29
39
  rexml
30
40
  kramdown-parser-gfm (1.1.0)
31
41
  kramdown (~> 2.0)
32
- mini_portile2 (2.8.4)
33
- minitest (5.18.0)
34
- nokogiri (1.14.2)
35
- mini_portile2 (~> 2.8.0)
42
+ language_server-protocol (3.17.0.3)
43
+ mini_portile2 (2.8.5)
44
+ minitest (5.22.2)
45
+ mutex_m (0.2.0)
46
+ nokogiri (1.16.2)
47
+ mini_portile2 (~> 2.8.2)
36
48
  racc (~> 1.4)
37
- nokogiri (1.14.2-arm64-darwin)
49
+ nokogiri (1.16.2-arm64-darwin)
38
50
  racc (~> 1.4)
39
- nokogiri (1.14.2-x86_64-linux)
51
+ nokogiri (1.16.2-x86_64-linux)
40
52
  racc (~> 1.4)
41
- nokolexbor (0.5.0)
42
- nokolexbor (0.5.0-arm64-darwin)
43
- nokolexbor (0.5.0-x86_64-linux)
44
- parallel (1.22.1)
45
- parser (3.2.2.0)
53
+ nokolexbor (0.5.2)
54
+ parallel (1.24.0)
55
+ parser (3.3.0.5)
46
56
  ast (~> 2.4.1)
47
- racc (1.6.2)
57
+ racc
58
+ racc (1.7.3)
48
59
  rainbow (3.1.1)
49
- rake (13.0.6)
50
- regexp_parser (2.7.0)
60
+ rake (13.1.0)
61
+ regexp_parser (2.9.0)
51
62
  reverse_markdown (2.1.1)
52
63
  nokogiri
53
- rexml (3.2.5)
54
- rubocop (1.49.0)
64
+ rexml (3.2.6)
65
+ rubocop (1.60.2)
55
66
  json (~> 2.3)
67
+ language_server-protocol (>= 3.17.0)
56
68
  parallel (~> 1.10)
57
- parser (>= 3.2.0.0)
69
+ parser (>= 3.3.0.2)
58
70
  rainbow (>= 2.2.2, < 4.0)
59
71
  regexp_parser (>= 1.8, < 3.0)
60
72
  rexml (>= 3.2.5, < 4.0)
61
- rubocop-ast (>= 1.28.0, < 2.0)
73
+ rubocop-ast (>= 1.30.0, < 2.0)
62
74
  ruby-progressbar (~> 1.7)
63
75
  unicode-display_width (>= 2.4.0, < 3.0)
64
- rubocop-ast (1.28.0)
76
+ rubocop-ast (1.30.0)
65
77
  parser (>= 3.2.1.0)
66
78
  rubocop-minitest (0.22.2)
67
79
  rubocop (>= 0.90, < 2.0)
68
80
  rubocop-rake (0.6.0)
69
81
  rubocop (~> 1.0)
70
82
  ruby-progressbar (1.13.0)
83
+ ruby2_keywords (0.0.5)
71
84
  solargraph (0.47.2)
72
85
  backport (~> 1.2)
73
86
  benchmark
@@ -83,14 +96,12 @@ GEM
83
96
  thor (~> 1.0)
84
97
  tilt (~> 2.0)
85
98
  yard (~> 0.9, >= 0.9.24)
86
- thor (1.2.1)
87
- tilt (2.1.0)
99
+ thor (1.3.0)
100
+ tilt (2.3.0)
88
101
  tzinfo (2.0.6)
89
102
  concurrent-ruby (~> 1.0)
90
- unicode-display_width (2.4.2)
91
- webrick (1.7.0)
92
- yard (0.9.28)
93
- webrick (~> 1.7.0)
103
+ unicode-display_width (2.5.0)
104
+ yard (0.9.34)
94
105
 
95
106
  PLATFORMS
96
107
  arm64-darwin-21
data/heartml.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = "Server-rendered web components"
12
12
  spec.homepage = "https://github.com/heartml/heartml-ruby#readme"
13
13
  spec.license = "MIT"
14
- spec.required_ruby_version = ">= 3.0"
14
+ spec.required_ruby_version = ">= 3.1"
15
15
 
16
16
  spec.metadata["homepage_uri"] = spec.homepage
17
17
  spec.metadata["source_code_uri"] = "https://github.com/heartml/heartml-ruby"
@@ -29,6 +29,4 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency "concurrent-ruby", "~> 1.2"
31
31
  spec.add_dependency "nokolexbor", ">= 0.4.2"
32
-
33
- spec.add_development_dependency "hash_with_dot_access", "~> 1.2"
34
32
  end
@@ -11,7 +11,6 @@ module Heartml
11
11
  view_context = Bridgetown::ERBView.new(resource)
12
12
 
13
13
  rdr = Heartml::TemplateRenderer.new(body: doc.at_css("body"), context: view_context)
14
- # rdr.define_singleton_method(:view_context) { view_context }
15
14
  rdr.call
16
15
  end
17
16
  end
@@ -19,6 +18,7 @@ module Heartml
19
18
  end
20
19
 
21
20
  Bridgetown.initializer :heartml do |config|
21
+ # TODO: maybe we can ditch this once we're off ActiveSupport in Bridgetown
22
22
  Bridgetown::Component.extend ActiveSupport::DescendantsTracker
23
23
 
24
24
  Heartml.module_eval do
@@ -13,9 +13,9 @@ module Heartml
13
13
  # NOTE: for some reason, the traverse method yields node children first, then the
14
14
  # parent node. That doesn't work for our case. We want to go strictly in source order.
15
15
  # So this is our own implementation of that.
16
- def traverse(node, &block)
16
+ def traverse(node, &block) # rubocop:disable Style/ArgumentsForwarding
17
17
  yield(node)
18
- node.children.each { |child| traverse(child, &block) }
18
+ node.children.each { |child| traverse(child, &block) } # rubocop:disable Style/ArgumentsForwarding
19
19
  end
20
20
 
21
21
  def process(fragment = @fragment) # rubocop:disable Metrics
@@ -79,7 +79,7 @@ module Heartml
79
79
  next unless attribute_binding.matcher.match?(name)
80
80
  next if attribute_binding.method.receiver._check_stack(node)
81
81
 
82
- break unless attribute_binding.method.(attribute: attr_node, node: node)
82
+ break unless attribute_binding.method.(attribute: attr_node, node:)
83
83
  end
84
84
  rescue Exception => e # rubocop:disable Lint/RescueException
85
85
  line_segments = [@component.class.heart_module, @component._line_number_of_node(attr_node)]
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Heartml
4
+ class Railtie < ::Rails::Railtie
5
+ module ReloadDocsInDevelopment
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ before_action :reload_docs
10
+ end
11
+
12
+ def reload_docs
13
+ Heartml.registered_elements.each { _1.instance_variable_set(:@doc, nil) }
14
+ end
15
+ end
16
+
17
+ initializer "heartml.reload_docs_in_development" do |_app|
18
+ unless Rails.env.production?
19
+ ActiveSupport.on_load(:action_controller_base) do
20
+ include ReloadDocsInDevelopment
21
+ end
22
+ end
23
+ end
24
+
25
+ config.to_prepare do
26
+ next if Rails.env.production?
27
+
28
+ components_folder = Rails.root.join("app", "components")
29
+ unless File.directory?(components_folder)
30
+ Rails.logger.error "Heartml: missing `app/components' folder, cannot load elements"
31
+ next
32
+ end
33
+
34
+ Rails.autoloaders.main.eager_load_dir components_folder
35
+ end
36
+ end
37
+ end
@@ -36,7 +36,7 @@ module Heartml
36
36
 
37
37
  # @param klass [Class]
38
38
  # @return [void]
39
- def self.included(klass) # rubocop:disable Metrics
39
+ def self.included_extras(klass) # rubocop:disable Metrics
40
40
  klass.attribute_binding "server-effect", :_server_effect_binding
41
41
  klass.attribute_binding "iso-effect", :_iso_effect_binding
42
42
 
@@ -58,7 +58,7 @@ module Heartml
58
58
  end
59
59
 
60
60
  def _server_effect_binding(attribute:, node:)
61
- _iso_effect_binding(attribute: attribute, node: node)
61
+ _iso_effect_binding(attribute:, node:)
62
62
  node.remove_attribute "host-effect"
63
63
  end
64
64
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Heartml
4
+ class ServerComponent
5
+ def self.source_location
6
+ caller_locations(1, 10).reject do |l|
7
+ l.label == "inherited"
8
+ end[1].absolute_path
9
+ end
10
+
11
+ def self.inherited(klass)
12
+ super
13
+ klass.include Heartml
14
+ end
15
+ end
16
+
17
+ class TemplateRenderer < ServerComponent
18
+ def self.heart_module
19
+ "eval"
20
+ end
21
+
22
+ def initialize(body:, context:) # rubocop:disable Lint/MissingSuper
23
+ @doc_html = body.is_a?(String) ? body : body.to_html
24
+ @body = body.is_a?(String) ? Nokolexbor::DocumentFragment.parse(body) : body
25
+ @context = context
26
+ end
27
+
28
+ def call
29
+ Fragment.new(@body, self).process
30
+ @body
31
+ end
32
+
33
+ def respond_to_missing?(key)
34
+ context.respond_to?(key)
35
+ end
36
+
37
+ # TODO: delegate instead?
38
+ def method_missing(key, *args, **kwargs)
39
+ context.send(key, *args, **kwargs)
40
+ end
41
+ end
42
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Heartml
4
4
  # @return [String]
5
- VERSION = "1.0.0.beta6"
5
+ VERSION = "1.0.0.beta8"
6
6
  end
data/lib/heartml.rb CHANGED
@@ -26,6 +26,8 @@ module Heartml
26
26
  require_relative "heartml/query_selection"
27
27
  require "heartml/server_effects"
28
28
 
29
+ include ServerEffects
30
+
29
31
  def self.registered_elements
30
32
  @registered_elements ||= Concurrent::Set.new
31
33
 
@@ -50,8 +52,9 @@ module Heartml
50
52
  def self.included(klass)
51
53
  klass.extend ClassMethods
52
54
 
53
- klass.attribute_binding "server-children", :_server_children_binding, only: :template
54
- klass.attribute_binding "server-unsafe-eval", :_server_replace_binding
55
+ klass.attribute_binding "server-children", :_server_children, only: :template
56
+ klass.attribute_binding "server-content", :_server_content, only: :template
57
+ klass.attribute_binding "server-unsafe-eval", :_server_unsafe_eval
55
58
 
56
59
  # Don't stomp on a superclass's `content` method
57
60
  has_content_method = begin
@@ -61,6 +64,8 @@ module Heartml
61
64
  end
62
65
 
63
66
  klass.include ContentMethod unless has_content_method
67
+
68
+ ServerEffects.included_extras(klass)
64
69
  end
65
70
 
66
71
  # Extends the component class
@@ -112,6 +117,8 @@ module Heartml
112
117
  end
113
118
  end
114
119
 
120
+ def output_tag_name(value = nil) = @output_tag_name ||= value
121
+
115
122
  # @param value [String]
116
123
  # @return [String]
117
124
  def heart_module(value = nil) = @heart_module ||= value
@@ -136,7 +143,7 @@ module Heartml
136
143
  def attribute_binding(matcher, method_name, only: nil)
137
144
  attribute_bindings << AttributeBinding.new(
138
145
  matcher: Regexp.new(matcher),
139
- method_name: method_name,
146
+ method_name:,
140
147
  only_for_tag: only
141
148
  )
142
149
  end
@@ -247,7 +254,17 @@ module Heartml
247
254
  tmpl_el.remove
248
255
  end
249
256
 
250
- rendering_mode == :node ? doc : doc.to_html
257
+ if self.class.output_tag_name
258
+ swap_doc = doc.document.create_element(self.class.output_tag_name)
259
+ doc.attrs.each do |attr, value|
260
+ swap_doc[attr] = value
261
+ end
262
+ swap_doc << doc.children
263
+
264
+ rendering_mode == :node ? swap_doc : swap_doc.to_html
265
+ else
266
+ rendering_mode == :node ? doc : doc.to_html
267
+ end
251
268
  end
252
269
 
253
270
  def call(...) = render_element(...)
@@ -319,7 +336,7 @@ module Heartml
319
336
  # TODO: handle objects inside of an array?
320
337
  obj
321
338
  else
322
- Array[obj]
339
+ [obj]
323
340
  end.join(" ")
324
341
  end
325
342
 
@@ -353,12 +370,12 @@ module Heartml
353
370
  @_context_locals = previous_context
354
371
  end
355
372
 
356
- def _server_children_binding(attribute:, node:) # rubocop:disable Lint/UnusedMethodArgument
373
+ def _server_children(attribute:, node:) # rubocop:disable Lint/UnusedMethodArgument
357
374
  self.replaced_content = node.children[0]
358
375
  node.remove
359
376
  end
360
377
 
361
- def _server_replace_binding(attribute:, node:)
378
+ def _server_unsafe_eval(attribute:, node:)
362
379
  node_name = node.name
363
380
  correct_node = node_name == "template" ? node.children[0] : node
364
381
  result = node_or_string(evaluate_attribute_expression(attribute, correct_node.inner_html))
@@ -371,39 +388,12 @@ module Heartml
371
388
  end
372
389
  end
373
390
 
374
- class ServerComponent
375
- def self.inherited(klass)
376
- super
377
- klass.include Heartml
378
- klass.include Heartml::ServerEffects
379
- end
380
- end
381
-
382
- class TemplateRenderer < ServerComponent
383
- def self.heart_module
384
- "eval"
385
- end
386
-
387
- def initialize(body:, context:) # rubocop:disable Lint/MissingSuper
388
- @doc_html = body.is_a?(String) ? body : body.to_html
389
- @body = body.is_a?(String) ? Nokolexbor::DocumentFragment.parse(body) : body
390
- @context = context
391
- end
392
-
393
- def call
394
- Fragment.new(@body, self).process
395
- @body
396
- end
397
-
398
- def respond_to_missing?(key)
399
- context.respond_to?(key)
400
- end
401
-
402
- # TODO: delegate instead?
403
- def method_missing(key, *args, **kwargs)
404
- context.send(key, *args, **kwargs)
405
- end
391
+ def _server_content(attribute:, node:)
392
+ result = evaluate_attribute_expression(attribute, "content")
393
+ node.swap(result)
406
394
  end
407
395
  end
408
396
 
397
+ require_relative "heartml/template_renderer"
409
398
  require_relative "heartml/bridgetown_renderer" if defined?(Bridgetown)
399
+ require_relative "heartml/railtie" if defined?(Rails::Railtie)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heartml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta6
4
+ version: 1.0.0.beta8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jared White
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-06 00:00:00.000000000 Z
11
+ date: 2024-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.4.2
41
- - !ruby/object:Gem::Dependency
42
- name: hash_with_dot_access
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '1.2'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '1.2'
55
41
  description:
56
42
  email:
57
43
  - jared@whitefusion.studio
@@ -72,9 +58,10 @@ files:
72
58
  - lib/heartml.rb
73
59
  - lib/heartml/bridgetown_renderer.rb
74
60
  - lib/heartml/fragment.rb
75
- - lib/heartml/petite.rb
76
61
  - lib/heartml/query_selection.rb
62
+ - lib/heartml/railtie.rb
77
63
  - lib/heartml/server_effects.rb
64
+ - lib/heartml/template_renderer.rb
78
65
  - lib/heartml/version.rb
79
66
  homepage: https://github.com/heartml/heartml-ruby#readme
80
67
  licenses:
@@ -92,14 +79,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
79
  requirements:
93
80
  - - ">="
94
81
  - !ruby/object:Gem::Version
95
- version: '3.0'
82
+ version: '3.1'
96
83
  required_rubygems_version: !ruby/object:Gem::Requirement
97
84
  requirements:
98
- - - ">"
85
+ - - ">="
99
86
  - !ruby/object:Gem::Version
100
- version: 1.3.1
87
+ version: '0'
101
88
  requirements: []
102
- rubygems_version: 3.3.3
89
+ rubygems_version: 3.5.3
103
90
  signing_key:
104
91
  specification_version: 4
105
92
  summary: Server-rendered web components
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "heartml"
4
-
5
- module Heartml
6
- module Petite
7
- # @param klass [Class]
8
- # @return [void]
9
- def self.included(klass)
10
- klass.attribute_binding "v-for", :_petite_for_binding, only: :template
11
- klass.attribute_binding "v-text", :_petite_text_binding
12
- klass.attribute_binding "v-html", :_petite_html_binding
13
- klass.attribute_binding "v-bind", :_petite_bound_attribute
14
- klass.attribute_binding %r{^:}, :_petite_bound_attribute
15
- end
16
-
17
- protected
18
-
19
- def _petite_for_binding(attribute:, node:)
20
- delimiter = node["v-for"].include?(" of ") ? " of " : " in "
21
- expression = node["v-for"].split(delimiter)
22
-
23
- process_list(
24
- attribute: attribute,
25
- node: node,
26
- item_node: node.children[0].first_element_child,
27
- for_in: expression
28
- )
29
- end
30
-
31
- def _petite_text_binding(attribute:, node:)
32
- node.content = evaluate_attribute_expression(attribute).to_s
33
- end
34
-
35
- def _petite_html_binding(attribute:, node:)
36
- node.inner_html = evaluate_attribute_expression(attribute).to_s
37
- end
38
-
39
- def _petite_bound_attribute(attribute:, node:) # rubocop:disable Metrics
40
- return if attribute.name == ":key"
41
-
42
- real_attribute = if attribute.name.start_with?(":")
43
- attribute.name.delete_prefix(":")
44
- elsif attribute.name.start_with?("v-bind:")
45
- attribute.name.delete_prefix("v-bind:")
46
- end
47
-
48
- obj = evaluate_attribute_expression(attribute)
49
-
50
- if real_attribute == "class"
51
- node[real_attribute] = class_list_for(obj)
52
- elsif real_attribute != "style" # style bindings aren't SSRed
53
- node[real_attribute] = obj if obj
54
- end
55
- end
56
- end
57
- end