svelte-on-rails 5.3.2 → 6.0.0

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: 66ba05e463ee27dcf6234fd49f46f1879daecd331f52549080d1d9f2f9e0748d
4
- data.tar.gz: 1947be68d44b723ed074098728db6a0aaba6e5c5bbbb72dc09604b5fc4dc93f3
3
+ metadata.gz: 72ff2251a05680fdb51fa556c72aaba218728a0040265412544d838ef7a4b4e7
4
+ data.tar.gz: 394c75169ba815b224e74f5b4c3e2675f50845505f4e00c52bdecf363b9db4ba
5
5
  SHA512:
6
- metadata.gz: 3c4a60a0c0f2d372535af2a7972c4df70ecb2958e0f0f2e9c718f6aeeccbab8f2797c329ec8ea789e5aa9d9e5154bc5b5cc88f70d0a5211416aa1d5d0296d8ad
7
- data.tar.gz: afa1cef4cef2787e3da4d0a040b6261f1313b503ae149238faa74f779c46a5d78a569a4ac98c84d0e76276c514d8940f9dd6b6086a58c9c7e54490a2671d18a1
6
+ metadata.gz: cfbbe11843b3b00a104bba37d942c98f5e49aca735f0b933475a3ee77b495418758eb83638b073dd7ed5e866a32bacc4c434e6daaa33e7b98303aa8731093bfe
7
+ data.tar.gz: a228c78ccf69847ff2f61d360734af6d1e7cb912b3928d6af056585fa1cabc0d121405c5d7b9099b659d15143a1771937e6d3745aa80bfd8a39d73e0fea66b0a
data/README.md CHANGED
@@ -302,13 +302,18 @@ But, in normal cases it should not be neccessary testing ssr explicitly.
302
302
 
303
303
  ## ActiveRecord helpers
304
304
 
305
- Adds the `#svelte_attributes` helper to your models, example:
305
+ Adds the `#to_svelte` helper to your models, example:
306
306
 
307
307
  ```ruby
308
- @book.svelte_attributes(
308
+ @book.to_svelte(
309
309
  :name,
310
310
  :calculation_method,
311
- author: [:name]
311
+ author: [:name],
312
+ editions: [
313
+ :date,
314
+ offset: 2,
315
+ limit: 1
316
+ ]
312
317
  )
313
318
  ```
314
319
 
@@ -322,6 +327,11 @@ would result in something like this:
322
327
  "author" => {
323
328
  "name" => "Michael Hartl"
324
329
  },
330
+ "editions" => [
331
+ {
332
+ date: "2025-02-03"
333
+ }
334
+ ]
325
335
  },
326
336
  "book_labels" => {
327
337
  "name" => "Name", # translated by human_attribute_name..
@@ -330,15 +340,35 @@ would result in something like this:
330
340
  },
331
341
  "author_labels": {
332
342
  "name" => "Name"
343
+ },
344
+ "edition_labels" => {
345
+ "date" => "Date"
333
346
  }
334
347
  }
335
348
  ```
336
349
 
337
350
  This should ease transferring data you need within the component mostly.
338
351
 
339
- If an optional `belongs_to` association is empty, the labels are still calculated.
352
+ The same method is applicable for:
353
+
354
+ - The model itself
355
+ - It returns only the labels: Same result like above, but without the `values` key
356
+ - `ActiveRecord::Relation`
357
+ - Same result like above, but `values` then is a Array.
358
+
359
+ If a association returns a empty result, the labels are still calculated.
360
+
361
+ `offset` and `limit` are reserved keys, so, columns with the same name would be ignored.
362
+
363
+ **Caching:**
364
+
365
+ Caching Capability is not implemented on this method, you easily can wrap it by `Redis`
340
366
 
341
- **Caching:** The component's attributes are used to generate a checksum, which serves as the cache key for efficient storage and retrieval.
367
+ If used on the `cached_svelte_component` view helper,
368
+ the component's attributes are used to generate a checksum, which serves as the
369
+ cache key for efficient storage and retrieval. So, this method is meant to
370
+ make it easier to exactly filter out only the information that is needed
371
+ on the component.
342
372
 
343
373
  ## Caching
344
374
 
@@ -10,6 +10,7 @@ require "svelte_on_rails/renderer/renderer"
10
10
 
11
11
  require "svelte_on_rails/lib/utils"
12
12
  require "svelte_on_rails/lib/view_helper_support"
13
+ require "svelte_on_rails/lib/to_svelte"
13
14
 
14
15
  # installer
15
16
  require 'svelte_on_rails/installer/utils'
@@ -1,41 +1,40 @@
1
1
  # lib/svelte_on_rails/active_record_extensions.rb
2
2
  module SvelteOnRails
3
3
  module ActiveRecordExtensions
4
- def self.included(base)
5
- unless defined?(ActiveRecord::Base) && base.ancestors.include?(ActiveRecord::Base)
6
- raise 'SvelteOnRails::ActiveRecordExtensions can only be included in ActiveRecord models'
7
- end
8
- end
4
+ # def self.included(base)
5
+ # unless defined?(ActiveRecord::Base) && base.ancestors.include?(ActiveRecord::Base)
6
+ # raise 'SvelteOnRails::ActiveRecordExtensions can only be included in ActiveRecord models'
7
+ # end
8
+ # end
9
9
 
10
10
  # Returns a hash of attributes, methods, and associations formatted for Svelte components
11
- def svelte_attributes(*attributes)
12
- @svelte_attributes ||= begin
11
+ def to_svelte(*attributes)
12
+ @to_svelte ||= begin
13
13
 
14
- # separate offset and limit
14
+ cl = SvelteOnRails::Lib::SvelteAttributes
15
+ cl.new.calculate_instance(self, attributes)
15
16
 
16
- h = attributes.grep(Hash)
17
- attr = if h.present?
18
- opts = h.first.symbolize_keys
19
- offset = opts[:offset]
20
- limit = opts[:limit]
21
- if offset || limit
22
- unless self.respond_to?(:each)
23
- raise 'offset and limit are only supported for record sets that respond to :each'
24
- end
25
- _opts = opts.reject { |key, _| [:offset, :limit].include?(key) }
26
- attributes.select { |item| !item.is_a?(Hash) }.push(_opts)
27
- else
28
- attributes
29
- end
30
- else
31
- attributes
32
- end
17
+ end
18
+ end
33
19
 
34
- utils = SvelteOnRails::Lib::Utils
35
- utils.svelte_attributes(self, attr, offset: offset, limit: limit)
20
+ end
36
21
 
22
+ module ActiveRecordClassExtensions
23
+ def to_svelte(*attributes)
24
+ @to_svelte ||= begin
25
+ cl = SvelteOnRails::Lib::SvelteAttributes
26
+ cl.new.calculate_class(self, attributes)
37
27
  end
38
28
  end
29
+ end
39
30
 
31
+ module ActiveRecordRelationExtensions
32
+ def to_svelte(*attributes)
33
+ @to_svelte ||= begin
34
+ cl = SvelteOnRails::Lib::SvelteAttributes
35
+ cl.new.calculate_relation(self, attributes)
36
+ end
37
+ end
40
38
  end
39
+
41
40
  end
@@ -0,0 +1,190 @@
1
+ module SvelteOnRails
2
+ module Lib
3
+ class SvelteAttributes
4
+
5
+ def initialize
6
+ @labels = {}
7
+ end
8
+
9
+ def calculate_instance(record, attributes, call_stack: 0, offset: nil, limit: nil)
10
+
11
+ next_stack = call_stack + 1
12
+
13
+ if record.respond_to?(:each)
14
+
15
+ recs2 = if offset || limit
16
+ if (record.is_a?(ActiveRecord::Relation) || record.is_a?(ActiveRecord::Associations::CollectionProxy) rescue false)
17
+ _recs = (offset ? record.offset(offset) : record)
18
+ (limit ? _recs.limit(limit) : _recs)
19
+ elsif record.respond_to?(:drop) && record.respond_to?(:take) # that might be a array
20
+ _recs = offset ? record.drop(offset) : record
21
+ limit ? _recs.take(limit) : _recs
22
+ else
23
+ raise "[svelte-on-rails:to_svelte] unknown class for records: #{record}"
24
+ end
25
+ else
26
+ record
27
+ end
28
+
29
+ set_labels(record.first, attributes)
30
+
31
+ values = recs2.map do |rec|
32
+ calculate_instance(rec, attributes, call_stack: next_stack)
33
+ end
34
+
35
+ else
36
+
37
+ # we have a single record
38
+
39
+ values = {}
40
+
41
+ set_labels(record, attributes)
42
+
43
+ attributes.each do |attr|
44
+
45
+ if attr.is_a? Hash
46
+
47
+ # we have associations
48
+ attr.each do |key, value|
49
+ next if ['offset', 'limit'].include?(key.to_s)
50
+ _offset, _limit, _value = extract_limit(value)
51
+
52
+ _key = key.to_s
53
+
54
+ # inspect association and set_labels
55
+
56
+ reflect = record.class.reflect_on_association(_key)
57
+ raise "invalid association: #{_key}" unless reflect
58
+ set_labels(reflect, value)
59
+
60
+ # values
61
+
62
+ recs = record.send(_key)
63
+ if recs.present?
64
+ if recs.respond_to?(:each)
65
+ values[_key] = calculate_instance(
66
+ recs,
67
+ value,
68
+ call_stack: next_stack,
69
+ offset: _offset,
70
+ limit: _limit
71
+ )
72
+ else
73
+ values[_key] = calculate_instance(
74
+ recs,
75
+ value,
76
+ call_stack: next_stack
77
+ )
78
+ end
79
+ end
80
+ end
81
+
82
+ else
83
+ # we have attributes
84
+ raise 'Invalid attribute' unless [Symbol, String].include?(attr.class)
85
+ _key = attr.to_s
86
+
87
+ values[_key] = record.send(_key)
88
+ end
89
+
90
+ end
91
+ end
92
+
93
+ if call_stack >= 1
94
+ values
95
+ else
96
+ { 'values' => values }.merge(@labels)
97
+ end
98
+
99
+ end
100
+
101
+ def calculate_class(model, attributes, call_stack: 0)
102
+
103
+ next_stack = call_stack + 1
104
+
105
+ set_labels(model, attributes)
106
+
107
+ hash = attributes.find { |element| element.is_a?(Hash) } || {}
108
+ hash.each do |key, value|
109
+ reflect = model.reflect_on_association(key.to_s)
110
+ if reflect
111
+ calculate_class(
112
+ reflect,
113
+ value,
114
+ call_stack: next_stack
115
+ )
116
+ end
117
+ end
118
+
119
+ if call_stack == 0
120
+ @labels
121
+ end
122
+ end
123
+
124
+ def calculate_relation(relation, attributes, call_stack: 0)
125
+ set_labels(relation.klass, attributes)
126
+ values = relation.map do |rec|
127
+ calculate_instance(rec, attributes)['values']
128
+ end
129
+ {
130
+ 'values' => values
131
+ }.merge(@labels)
132
+ end
133
+
134
+ private
135
+
136
+ def extract_limit(attributes)
137
+
138
+ _hash_args = attributes.grep(Hash).first.dup
139
+ attr, lim = if _hash_args.present?
140
+ hash_args = _hash_args.transform_keys { |key| key.to_s } # multiple arrays is not possible
141
+ hash_remainder = hash_args.reject { |key, _| %w[offset limit].include?(key.to_s) }
142
+ _attr = attributes.reject { |item| item.is_a?(Hash) }
143
+ [
144
+ if hash_remainder.any?
145
+ _attr + [hash_remainder]
146
+ else
147
+ _attr
148
+ end,
149
+ hash_args
150
+ ]
151
+ else
152
+ [
153
+ attributes,
154
+ {}
155
+ ]
156
+ end
157
+
158
+ [
159
+ lim['offset'],
160
+ lim['limit'],
161
+ attr
162
+ ]
163
+ end
164
+
165
+ def set_labels(record, keys)
166
+
167
+ first_hash = keys.find { |element| element.is_a?(Hash) }
168
+ _keys = keys.reject { |element| element.is_a?(Hash) }
169
+ _keys += first_hash.keys if first_hash
170
+
171
+ _keys.each do |attr|
172
+ unless attr.respond_to?(:each)
173
+ obj = if record.respond_to?(:human_attribute_name)
174
+ record
175
+ elsif record.class.respond_to?(:human_attribute_name)
176
+ record.class
177
+ end
178
+
179
+ next unless obj
180
+
181
+ @labels["#{obj.to_s.underscore}_labels"] ||= {}
182
+ @labels["#{obj.to_s.underscore}_labels"][attr.to_s] ||= obj.human_attribute_name(attr)
183
+
184
+ end
185
+ end
186
+ end
187
+
188
+ end
189
+ end
190
+ end
@@ -163,132 +163,6 @@ module SvelteOnRails
163
163
  end
164
164
  end
165
165
 
166
- def self.svelte_attributes(record, attributes, labels: {}, call_stack: 0, offset: nil, limit: nil)
167
-
168
- if record.is_a?(Class)
169
-
170
- # this is the case if a belongs_to association is empty; In that case we pass the class itself and extract only the labels
171
- raise 'limit and offset are supported only for iterable objects' if limit || offset
172
- attributes.each do |attr|
173
- unless attr.respond_to?(:each)
174
- labels["#{record.to_s.underscore}_labels"] ||= {}
175
- labels["#{record.to_s.underscore}_labels"][attr.to_s] ||= record.human_attribute_name(attr)
176
- end
177
- end
178
- values = {}
179
-
180
- elsif record.respond_to?(:each)
181
-
182
- unless record.is_a?(ActiveRecord::Relation) || record.is_a?(ActiveRecord::Associations::CollectionProxy)
183
- raise 'record set must be a Article::ActiveRecord_Associations_CollectionProxy'
184
- end
185
-
186
- recs = (offset ? record.offset(offset) : record)
187
- recs2 = (limit ? recs.limit(limit) : recs)
188
-
189
- values = recs2.map do |rec|
190
- svelte_attributes(rec, attributes, labels: labels, call_stack: call_stack + 1)
191
- end
192
-
193
- else
194
-
195
- # we have a single record
196
-
197
- raise 'limit and offset are supported only for iterable objects' if limit || offset
198
- values = {}
199
-
200
- table_name = record.class.to_s.underscore
201
-
202
- attributes.each do |attr|
203
-
204
- if attr.is_a? Hash
205
-
206
- # we have associations
207
- attr.each do |key, value|
208
- _key = key.to_s
209
-
210
- # skip and fetch offset and limit
211
- if key.to_s.match(/_limit$/) && !record.respond_to?(key)
212
- raise 'Invalid attribute' unless record.respond_to?(key.to_s[0..-7])
213
- next
214
- end
215
- if key.to_s.match(/_offset$/) && !record.respond_to?(key)
216
- raise 'Invalid attribute' unless record.respond_to?(key.to_s[0..-8])
217
- next
218
- end
219
- offs, lim = svelte_attribute_extract_limit(record, attributes, _key)
220
-
221
- labels["#{table_name}_labels"] ||= {}
222
- labels["#{table_name}_labels"][_key] ||= record.class.human_attribute_name(_key)
223
-
224
- # values
225
-
226
- reflect = record.class.reflect_on_association(_key)
227
- if reflect
228
- recs = record.send(_key)
229
- content = (recs.present? ? recs : reflect.active_record) # if no record, we extract only the labels
230
- if content.respond_to?(:each)
231
- values[_key] = svelte_attributes(
232
- content,
233
- value,
234
- labels: labels,
235
- call_stack: call_stack + 1,
236
- offset: offs,
237
- limit: lim
238
- )
239
- else
240
- values[_key] = svelte_attributes(
241
- content,
242
- value,
243
- labels: labels,
244
- call_stack: call_stack + 1,
245
- offset: offs,
246
- limit: lim
247
- )
248
- end
249
- else
250
- raise "invalid association: #{_key}"
251
- end
252
- end
253
-
254
- else
255
- # we have attributes
256
- raise 'Invalid attribute' unless [Symbol, String].include?(attr.class)
257
- _key = attr.to_s
258
-
259
- labels["#{table_name}_labels"] ||= {}
260
- labels["#{table_name}_labels"][_key] ||= record.class.human_attribute_name(_key)
261
-
262
- values[_key] = record.send(_key)
263
- end
264
-
265
- end
266
- end
267
-
268
- if call_stack >= 1
269
- values
270
- else
271
- { 'values' => values }.merge(labels)
272
- end
273
-
274
- end
275
-
276
- private
277
-
278
- def self.svelte_attribute_extract_limit(record, attributes, key)
279
-
280
- hash_args = attributes.grep(Hash).first.with_indifferent_access # multiple arrays is not possible
281
-
282
- offset = if hash_args["#{key}_offset"] && !record.respond_to?("#{key}_offset")
283
- hash_args["#{key}_offset"]
284
- end
285
-
286
- limit = if hash_args["#{key}_limit"] && !record.respond_to?("#{key}_limit")
287
- hash_args["#{key}_limit"]
288
- end
289
-
290
- [offset, limit]
291
- end
292
166
  end
293
167
  end
294
168
  end
@@ -15,10 +15,18 @@ module SvelteOnRails
15
15
 
16
16
  initializer 'svelte_on_rails.active_record_extensions' do
17
17
  ActiveSupport.on_load(:active_record) do
18
+ # Include instance-level extensions
18
19
  include SvelteOnRails::ActiveRecordExtensions
20
+ # Extend with class-level extensions
21
+ extend SvelteOnRails::ActiveRecordClassExtensions
19
22
  end
20
23
  end
21
24
 
25
+ # Extend ActiveRecord::Relation
26
+ ActiveSupport.on_load(:active_record_relation) do
27
+ include SvelteOnRails::ActiveRecordRelationExtensions
28
+ end
29
+
22
30
  rake_tasks do
23
31
  load File.expand_path("../../tasks/svelte_on_rails_tasks.rake", __FILE__)
24
32
  end
@@ -5,10 +5,6 @@ class SvelteOnRailsHelloWorldController < ApplicationController
5
5
 
6
6
  def web_socket_action
7
7
 
8
- # render plain: Article.first.svelte_attributes(:name) #(:name, :calc_method, children: [:name], children_limit: 2, parent: [:name]).to_json
9
- #
10
- # return
11
-
12
8
  comp = 'ReceiveFromChannel'
13
9
 
14
10
  case params['stream']
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: svelte-on-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.2
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Sedlmair
@@ -43,6 +43,7 @@ files:
43
43
  - lib/svelte_on_rails/installer/utils.rb
44
44
  - lib/svelte_on_rails/installer/vite.rb
45
45
  - lib/svelte_on_rails/lib/development_utils.rb
46
+ - lib/svelte_on_rails/lib/to_svelte.rb
46
47
  - lib/svelte_on_rails/lib/utils.rb
47
48
  - lib/svelte_on_rails/lib/view_helper_support.rb
48
49
  - lib/svelte_on_rails/railtie.rb