props_template 0.22.4 → 0.24.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 +4 -4
- data/README.md +24 -4
- data/lib/props_template/base.rb +8 -10
- data/lib/props_template/base_with_extensions.rb +7 -8
- data/lib/props_template/core_ext.rb +1 -1
- data/lib/props_template/dependency_tracker.rb +4 -5
- data/lib/props_template/extension_manager.rb +5 -7
- data/lib/props_template/extensions/cache.rb +12 -16
- data/lib/props_template/extensions/deferment.rb +5 -5
- data/lib/props_template/extensions/fragment.rb +3 -3
- data/lib/props_template/extensions/partial_renderer.rb +7 -9
- data/lib/props_template/handler.rb +3 -3
- data/lib/props_template/layout_patch.rb +16 -35
- data/lib/props_template/railtie.rb +4 -4
- data/lib/props_template/searcher.rb +6 -6
- data/lib/props_template/version.rb +1 -1
- data/lib/props_template.rb +17 -17
- metadata +3 -10
- data/lib/props_template/debug_writer.rb +0 -55
- data/spec/layout_spec.rb +0 -24
- data/spec/props_template_spec.rb +0 -300
- data/spec/searcher_spec.rb +0 -209
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed3fa0a5fda4993c730d2391de3e2a1d7f05e43580dc5e429734b5f8e7b07391
|
4
|
+
data.tar.gz: 005ed8fa15532e3e80530fae2a4c1bbd0002d9b9dcf5e2a41c64d34c62df2db0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a65e69050103126e02af67ebbf44af9817edd221467fef1ae6f9c715964b8c212ecc9d3a8796e9ec0868e395911668f11176dae3ce5c722620f9eccb326cede
|
7
|
+
data.tar.gz: b59b42c55eee9fb209093c38aeab2d369da005a6be41feecd73ecef62f6323b61ac41e879e005b40b584817d80e2b8873fb37c2c62dc22015177053371b5c08f
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ conveniences and magic.
|
|
29
29
|
json.flash flash.to_h
|
30
30
|
|
31
31
|
json.menu do
|
32
|
-
# all keys will be formatted as camelCase
|
32
|
+
# By default, all keys will be formatted as camelCase. See #change_key_format
|
33
33
|
|
34
34
|
json.current_user do
|
35
35
|
json.email current_user.email
|
@@ -94,7 +94,8 @@ You can also add a [layout](#layouts).
|
|
94
94
|
|
95
95
|
### json.set! or json.\<your key here\>
|
96
96
|
|
97
|
-
Defines the attribute or structure. All keys are automatically camelized lower
|
97
|
+
Defines the attribute or structure. All keys are automatically camelized lower
|
98
|
+
by default. See [Change Key Format](#change-key-format) to change this behavior.
|
98
99
|
|
99
100
|
```ruby
|
100
101
|
json.set! :author_details, {...options} do
|
@@ -584,6 +585,23 @@ json.flash flash.to_h
|
|
584
585
|
**NOTE** PropsTemplate inverts the usual Rails rendering flow. PropsTemplate
|
585
586
|
will render Layout first, then the template when `yield json` is used.
|
586
587
|
|
588
|
+
## Change key format
|
589
|
+
By default, all keys will be formatted with `camelized(:lower)`. If you want to
|
590
|
+
change this behavior, override it in an initializer:
|
591
|
+
|
592
|
+
```ruby
|
593
|
+
Props::BaseWithExtensions.class_eval do
|
594
|
+
def key_format(key)
|
595
|
+
key.to_s
|
596
|
+
end
|
597
|
+
end
|
598
|
+
```
|
599
|
+
|
600
|
+
## Escape mode
|
601
|
+
|
602
|
+
PropsTemplate runs OJ with `mode: :rails`, which escapes HTML and XML characters
|
603
|
+
such as `&` and `<`.
|
604
|
+
|
587
605
|
## Contributing
|
588
606
|
|
589
607
|
See the [CONTRIBUTING] document. Thank you, [contributors]!
|
@@ -593,7 +611,9 @@ See the [CONTRIBUTING] document. Thank you, [contributors]!
|
|
593
611
|
|
594
612
|
## Special Thanks
|
595
613
|
|
596
|
-
Thanks to [turbostreamer]
|
597
|
-
inspiration.
|
614
|
+
Thanks to [turbostreamer], [oj], and [jbuilder] for the inspiration.
|
598
615
|
|
599
616
|
[1]: https://github.com/thoughtbot/superglue
|
617
|
+
[turbostreamer]: https://github.com/malomalo/turbostreamer
|
618
|
+
[jbuilder]: https://github.com/rails/jbuilder
|
619
|
+
[oj]: https://github.com/ohler55/oj/
|
data/lib/props_template/base.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
#todo: active_support/core_ext/string/output_safety.rb
|
1
|
+
require "oj"
|
2
|
+
require "active_support"
|
4
3
|
|
5
4
|
module Props
|
6
5
|
class InvalidScopeForArrayError < StandardError; end
|
6
|
+
|
7
7
|
class InvalidScopeForObjError < StandardError; end
|
8
8
|
|
9
9
|
class Base
|
@@ -35,7 +35,7 @@ module Props
|
|
35
35
|
|
36
36
|
def set!(key, value = nil)
|
37
37
|
if @scope == :array
|
38
|
-
raise InvalidScopeForObjError.new(
|
38
|
+
raise InvalidScopeForObjError.new("Attempted to set! on an array! scope")
|
39
39
|
end
|
40
40
|
|
41
41
|
if @scope.nil?
|
@@ -81,19 +81,19 @@ module Props
|
|
81
81
|
collection.each_with_index do |item, index|
|
82
82
|
pass_opts = all_opts[index]
|
83
83
|
handle_collection_item(collection, item, index, pass_opts) do
|
84
|
-
#todo: remove index?
|
84
|
+
# todo: remove index?
|
85
85
|
yield item, index
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
#todo, add ability to define contents of array
|
90
|
+
# todo, add ability to define contents of array
|
91
91
|
def array!(collection, options = {})
|
92
92
|
if @scope.nil?
|
93
93
|
@scope = :array
|
94
94
|
@stream.push_array
|
95
95
|
else
|
96
|
-
raise InvalidScopeForArrayError.new(
|
96
|
+
raise InvalidScopeForArrayError.new("array! expects exclusive use of this block")
|
97
97
|
end
|
98
98
|
|
99
99
|
handle_collection(collection, options) do |item, index|
|
@@ -108,10 +108,8 @@ module Props
|
|
108
108
|
def result!
|
109
109
|
if @scope.nil?
|
110
110
|
@stream.push_object
|
111
|
-
@stream.pop
|
112
|
-
else
|
113
|
-
@stream.pop
|
114
111
|
end
|
112
|
+
@stream.pop
|
115
113
|
|
116
114
|
json = @stream.raw_json
|
117
115
|
@stream.reset
|
@@ -5,7 +5,7 @@ module Props
|
|
5
5
|
def initialize(builder, context = nil, options = {})
|
6
6
|
@context = context
|
7
7
|
@builder = builder
|
8
|
-
#todo: refactor so deferred can be its own class
|
8
|
+
# todo: refactor so deferred can be its own class
|
9
9
|
@em = ExtensionManager.new(self)
|
10
10
|
@traveled_path = []
|
11
11
|
@key_cache = {}
|
@@ -50,7 +50,7 @@ module Props
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def set!(key, options = {}, &block)
|
53
|
-
if
|
53
|
+
if block
|
54
54
|
options = @em.refine_options(options)
|
55
55
|
end
|
56
56
|
|
@@ -60,7 +60,7 @@ module Props
|
|
60
60
|
def handle_set_block(key, options)
|
61
61
|
@traveled_path.push(key)
|
62
62
|
n = 1
|
63
|
-
if suffix = options[:path_suffix]
|
63
|
+
if (suffix = options[:path_suffix])
|
64
64
|
n += suffix.length
|
65
65
|
@traveled_path.push(suffix)
|
66
66
|
end
|
@@ -68,7 +68,7 @@ module Props
|
|
68
68
|
super
|
69
69
|
|
70
70
|
@traveled_path.pop(n)
|
71
|
-
|
71
|
+
nil
|
72
72
|
end
|
73
73
|
|
74
74
|
def handle_collection_item(collection, item, index, options)
|
@@ -80,14 +80,14 @@ module Props
|
|
80
80
|
if id.nil?
|
81
81
|
@traveled_path.push(index)
|
82
82
|
else
|
83
|
-
@traveled_path.push("#{id
|
83
|
+
@traveled_path.push("#{id}=#{val}")
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
87
|
super
|
88
88
|
|
89
89
|
@traveled_path.pop
|
90
|
-
|
90
|
+
nil
|
91
91
|
end
|
92
92
|
|
93
93
|
def refine_all_item_options(all_options)
|
@@ -97,7 +97,7 @@ module Props
|
|
97
97
|
def refine_item_options(item, options)
|
98
98
|
return options if options.empty?
|
99
99
|
|
100
|
-
if key = options[:key]
|
100
|
+
if (key = options[:key])
|
101
101
|
val = if item.respond_to? key
|
102
102
|
item.send(key)
|
103
103
|
elsif item.is_a? Hash
|
@@ -111,4 +111,3 @@ module Props
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
114
|
-
|
@@ -1,16 +1,15 @@
|
|
1
1
|
# This was taken from jbuilder
|
2
|
-
require
|
3
|
-
|
2
|
+
require "props_template"
|
4
3
|
|
5
4
|
dependency_tracker = false
|
6
5
|
|
7
6
|
begin
|
8
|
-
require
|
9
|
-
require
|
7
|
+
require "action_view"
|
8
|
+
require "action_view/dependency_tracker"
|
10
9
|
dependency_tracker = ::ActionView::DependencyTracker
|
11
10
|
rescue LoadError
|
12
11
|
begin
|
13
|
-
require
|
12
|
+
require "cache_digests"
|
14
13
|
dependency_tracker = ::CacheDigests::DependencyTracker
|
15
14
|
rescue LoadError
|
16
15
|
end
|
@@ -2,7 +2,7 @@ module Props
|
|
2
2
|
class ExtensionManager
|
3
3
|
attr_reader :base, :builder, :context
|
4
4
|
|
5
|
-
def initialize(base, defered=[], fragments=[])
|
5
|
+
def initialize(base, defered = [], fragments = [])
|
6
6
|
@base = base
|
7
7
|
@context = base.context
|
8
8
|
@builder = base.builder
|
@@ -15,16 +15,14 @@ module Props
|
|
15
15
|
def refine_options(options, item = nil)
|
16
16
|
options = @partialer.refine_options(options, item)
|
17
17
|
options = @deferment.refine_options(options, item)
|
18
|
-
|
19
|
-
options
|
18
|
+
Cache.refine_options(options, item)
|
20
19
|
end
|
21
20
|
|
22
21
|
def refine_all_item_options(all_options)
|
23
22
|
return all_options if all_options.empty?
|
24
23
|
|
25
24
|
all_options = @partialer.find_and_add_template(all_options)
|
26
|
-
|
27
|
-
all_options
|
25
|
+
@cache.multi_fetch_and_add_results(all_options)
|
28
26
|
end
|
29
27
|
|
30
28
|
def deferred
|
@@ -79,10 +77,10 @@ module Props
|
|
79
77
|
yield
|
80
78
|
meta = Oj.dump([deferred_paths, fragment_paths]).strip
|
81
79
|
json_in_progress = base.stream.to_s
|
82
|
-
if json_in_progress[start] ==
|
80
|
+
if json_in_progress[start] == ","
|
83
81
|
start += 1
|
84
82
|
end
|
85
|
-
raw = base.stream.to_s[start
|
83
|
+
raw = base.stream.to_s[start..].strip
|
86
84
|
result = "#{meta}\n#{raw}"
|
87
85
|
}
|
88
86
|
result
|
@@ -21,9 +21,7 @@ module Props
|
|
21
21
|
@context = context
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
@context
|
26
|
-
end
|
24
|
+
attr_reader :context
|
27
25
|
|
28
26
|
def multi_fetch(keys, options = {})
|
29
27
|
result = {}
|
@@ -38,12 +36,12 @@ module Props
|
|
38
36
|
|
39
37
|
payload = {
|
40
38
|
controller_name: controller.controller_name,
|
41
|
-
action_name: controller.action_name
|
39
|
+
action_name: controller.action_name
|
42
40
|
}
|
43
41
|
|
44
42
|
read_caches = {}
|
45
43
|
|
46
|
-
ActiveSupport::Notifications.instrument(
|
44
|
+
ActiveSupport::Notifications.instrument("read_multi_fragments.action_view", payload) do |payload|
|
47
45
|
read_caches = ::Rails.cache.read_multi(*ckeys, options)
|
48
46
|
payload[:read_caches] = read_caches
|
49
47
|
end
|
@@ -60,31 +58,30 @@ module Props
|
|
60
58
|
first_opts = all_options[0]
|
61
59
|
|
62
60
|
if first_opts[:cache] && controller.perform_caching
|
63
|
-
keys = all_options.map{|i| i[:cache][0]}
|
61
|
+
keys = all_options.map { |i| i[:cache][0] }
|
64
62
|
c_opts = first_opts[:cache][1]
|
65
63
|
result = multi_fetch(keys, c_opts)
|
66
64
|
|
67
65
|
all_options.map do |opts|
|
68
|
-
key =
|
66
|
+
key = opts[:cache][0]
|
69
67
|
|
70
68
|
if result.key? key
|
71
69
|
opts[:cache][1][:result] = result[key]
|
72
|
-
opts
|
73
|
-
else
|
74
|
-
opts
|
75
70
|
end
|
71
|
+
|
72
|
+
opts
|
76
73
|
end
|
77
74
|
else
|
78
75
|
all_options
|
79
76
|
end
|
80
77
|
end
|
81
78
|
|
82
|
-
#Copied from jbuilder
|
79
|
+
# Copied from jbuilder
|
83
80
|
#
|
84
81
|
|
85
|
-
def cache(key=nil, options={})
|
82
|
+
def cache(key = nil, options = {})
|
86
83
|
if controller.perform_caching
|
87
|
-
|
84
|
+
cache_fragment_for(key, options) do
|
88
85
|
yield
|
89
86
|
end
|
90
87
|
else
|
@@ -120,12 +117,11 @@ module Props
|
|
120
117
|
|
121
118
|
if @context.respond_to?(:combined_fragment_cache_key)
|
122
119
|
key = @context.combined_fragment_cache_key(key)
|
123
|
-
|
124
|
-
key = url_for(key).split(
|
120
|
+
elsif ::Hash === key
|
121
|
+
key = url_for(key).split("://", 2).last
|
125
122
|
end
|
126
123
|
|
127
124
|
::ActiveSupport::Cache.expand_cache_key(key, :props)
|
128
125
|
end
|
129
126
|
end
|
130
127
|
end
|
131
|
-
|
@@ -17,7 +17,7 @@ module Props
|
|
17
17
|
}
|
18
18
|
|
19
19
|
if item
|
20
|
-
type = Proc === type ? type.call(item) : type
|
20
|
+
type = (Proc === type) ? type.call(item) : type
|
21
21
|
end
|
22
22
|
|
23
23
|
if type
|
@@ -44,10 +44,10 @@ module Props
|
|
44
44
|
end
|
45
45
|
|
46
46
|
request_path = @base.context.controller.request.fullpath
|
47
|
-
path = @base.traveled_path.join(
|
47
|
+
path = @base.traveled_path.join(".")
|
48
48
|
uri = ::URI.parse(request_path)
|
49
|
-
qry = ::URI.decode_www_form(uri.query ||
|
50
|
-
.reject{|x| x[0] ==
|
49
|
+
qry = ::URI.decode_www_form(uri.query || "")
|
50
|
+
.reject { |x| x[0] == "props_at" }
|
51
51
|
.push(["props_at", path])
|
52
52
|
|
53
53
|
uri.query = ::URI.encode_www_form(qry)
|
@@ -55,7 +55,7 @@ module Props
|
|
55
55
|
deferral = {
|
56
56
|
url: uri.to_s,
|
57
57
|
path: path,
|
58
|
-
type: type.to_s
|
58
|
+
type: type.to_s
|
59
59
|
}
|
60
60
|
|
61
61
|
# camelize for JS land
|
@@ -2,7 +2,7 @@ module Props
|
|
2
2
|
class Fragment
|
3
3
|
attr_reader :fragments
|
4
4
|
|
5
|
-
def initialize(base, fragments=[])
|
5
|
+
def initialize(base, fragments = [])
|
6
6
|
@base = base
|
7
7
|
@fragments = fragments
|
8
8
|
end
|
@@ -14,11 +14,11 @@ module Props
|
|
14
14
|
|
15
15
|
if String === fragment || Symbol === fragment
|
16
16
|
fragment_name = fragment.to_s
|
17
|
-
path = @base.traveled_path.join(
|
17
|
+
path = @base.traveled_path.join(".")
|
18
18
|
@name = fragment_name
|
19
19
|
|
20
20
|
@fragments.push(
|
21
|
-
{
|
21
|
+
{type: fragment_name, partial: partial_name, path: path}
|
22
22
|
)
|
23
23
|
end
|
24
24
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "action_view"
|
2
2
|
|
3
3
|
module Props
|
4
4
|
class RenderedTemplate
|
@@ -23,7 +23,6 @@ module Props
|
|
23
23
|
IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " \
|
24
24
|
"make sure your partial name starts with underscore."
|
25
25
|
|
26
|
-
|
27
26
|
def initialize(base, context, builder)
|
28
27
|
@context = context
|
29
28
|
@builder = builder
|
@@ -67,7 +66,7 @@ module Props
|
|
67
66
|
template_keys = retrieve_template_keys(partial_opts)
|
68
67
|
details = extract_details(partial_opts)
|
69
68
|
|
70
|
-
prefixes = partial.include?(
|
69
|
+
prefixes = partial.include?("/") ? [] : @context.lookup_context.prefixes
|
71
70
|
@context.lookup_context.find_template(partial, prefixes, true, template_keys, details)
|
72
71
|
end
|
73
72
|
|
@@ -81,13 +80,12 @@ module Props
|
|
81
80
|
partial, pass_opts = [*options[:partial]]
|
82
81
|
pass_opts ||= {}
|
83
82
|
pass_opts[:locals] ||= {}
|
84
|
-
pass_opts[:locals][:json] = @builder
|
85
83
|
pass_opts[:partial] = partial
|
86
84
|
pass_opts[:formats] = [:json]
|
87
85
|
pass_opts.delete(:handlers)
|
88
86
|
|
89
87
|
if !(String === partial)
|
90
|
-
raise ArgumentError.new(INVALID_PARTIAL_MESSAGE %
|
88
|
+
raise ArgumentError.new(INVALID_PARTIAL_MESSAGE % partial.inspect)
|
91
89
|
end
|
92
90
|
|
93
91
|
pass_opts
|
@@ -121,15 +119,15 @@ module Props
|
|
121
119
|
end
|
122
120
|
|
123
121
|
def raise_invalid_option_as(as)
|
124
|
-
raise ArgumentError.new(OPTION_AS_ERROR_MESSAGE %
|
122
|
+
raise ArgumentError.new(OPTION_AS_ERROR_MESSAGE % as)
|
125
123
|
end
|
126
124
|
|
127
125
|
def raise_invalid_identifier(path)
|
128
|
-
raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE %
|
126
|
+
raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % path)
|
129
127
|
end
|
130
128
|
|
131
129
|
def retrieve_variable(path)
|
132
|
-
base = path[-1] == "/" ? "" : File.basename(path)
|
130
|
+
base = (path[-1] == "/") ? "" : File.basename(path)
|
133
131
|
raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
|
134
132
|
$1.to_sym
|
135
133
|
end
|
@@ -153,7 +151,7 @@ module Props
|
|
153
151
|
|
154
152
|
locals[as] = item
|
155
153
|
|
156
|
-
if fragment_name = rest[:fragment]
|
154
|
+
if (fragment_name = rest[:fragment])
|
157
155
|
rest[:fragment] = fragment_name.to_s
|
158
156
|
end
|
159
157
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_support"
|
2
2
|
|
3
3
|
module Props
|
4
4
|
class Handler
|
@@ -8,8 +8,8 @@ module Props
|
|
8
8
|
def self.call(template, source = nil)
|
9
9
|
source ||= template.source
|
10
10
|
# this juggling is required to keep line numbers right in the error
|
11
|
-
%{
|
12
|
-
json.result!
|
11
|
+
%{ __finalize = !defined?(@__json); @__json ||= Props::Template.new(self); json = @__json; #{source};
|
12
|
+
json.result! if __finalize
|
13
13
|
}
|
14
14
|
end
|
15
15
|
end
|
@@ -1,54 +1,35 @@
|
|
1
1
|
module Props
|
2
2
|
module LayoutPatch
|
3
|
-
def
|
4
|
-
|
5
|
-
options[:locals][:json] = nil
|
6
|
-
|
7
|
-
@details = extract_details(options)
|
8
|
-
template = determine_template(options)
|
9
|
-
|
10
|
-
if template.respond_to?(:handler) && template.handler == Props::Handler && options[:layout]
|
3
|
+
def render_template(view, template, layout_name, locals)
|
4
|
+
if template.respond_to?(:handler) && template.handler == Props::Handler && layout_name
|
11
5
|
prepend_formats(template.format)
|
12
|
-
render_props_template(
|
6
|
+
render_props_template(view, template, layout_name, locals)
|
13
7
|
else
|
14
|
-
super
|
8
|
+
super
|
15
9
|
end
|
16
10
|
end
|
17
11
|
|
18
12
|
def render_props_template(view, template, path, locals)
|
13
|
+
view.instance_eval <<~RUBY, __FILE__, __LINE__ + 1
|
14
|
+
def virtual_path_of_template;"#{template.virtual_path}";end
|
15
|
+
RUBY
|
16
|
+
|
17
|
+
# Deprecated: Usage of virtual_path_of_template in local_assigns will
|
18
|
+
# be removed for a method definition above
|
19
19
|
layout_locals = locals.dup
|
20
|
-
layout_locals.delete(:json)
|
21
20
|
layout_locals[:virtual_path_of_template] = template.virtual_path
|
22
21
|
|
23
|
-
layout =
|
24
|
-
body = layout
|
25
|
-
|
22
|
+
layout = resolve_layout(path, layout_locals.keys, [formats.first])
|
23
|
+
body = if layout
|
24
|
+
layout.render(view, layout_locals) do |json|
|
25
|
+
template.render(view, locals)
|
26
|
+
end
|
27
|
+
else
|
26
28
|
template.render(view, locals)
|
27
29
|
end
|
28
30
|
|
29
31
|
build_rendered_template(body, template)
|
30
32
|
end
|
31
|
-
|
32
|
-
def resolve_props_layout(layout, keys, formats)
|
33
|
-
details = @details.dup
|
34
|
-
details[:formats] = formats
|
35
|
-
|
36
|
-
case layout
|
37
|
-
when String
|
38
|
-
begin
|
39
|
-
if layout.start_with?("/")
|
40
|
-
ActiveSupport::Deprecation.warn "Rendering layouts from an absolute path is deprecated."
|
41
|
-
@lookup_context.with_fallbacks.find_template(layout, nil, false, [], details)
|
42
|
-
else
|
43
|
-
@lookup_context.find_template(layout, nil, false, [], details)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
when Proc
|
47
|
-
resolve_layout(layout.call(@lookup_context, formats), keys, formats)
|
48
|
-
else
|
49
|
-
layout
|
50
|
-
end
|
51
|
-
end
|
52
33
|
end
|
53
34
|
end
|
54
35
|
|
@@ -1,13 +1,13 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rails/railtie"
|
2
|
+
require "props_template"
|
3
3
|
|
4
4
|
module Props
|
5
5
|
class Railtie < ::Rails::Railtie
|
6
6
|
initializer :props_template do
|
7
7
|
ActiveSupport.on_load :action_view do
|
8
8
|
ActionView::Template.register_template_handler :props, Props::Handler
|
9
|
-
require
|
10
|
-
require
|
9
|
+
require "props_template/dependency_tracker"
|
10
|
+
require "props_template/layout_patch"
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -2,7 +2,7 @@ module Props
|
|
2
2
|
class Searcher
|
3
3
|
attr_reader :builder, :context, :fragments, :traveled_path
|
4
4
|
|
5
|
-
def initialize(builder, path=[], context = nil)
|
5
|
+
def initialize(builder, path = [], context = nil)
|
6
6
|
@search_path = path
|
7
7
|
@depth = 0
|
8
8
|
@context = context
|
@@ -24,7 +24,7 @@ module Props
|
|
24
24
|
def found!
|
25
25
|
pass_opts = @found_options.clone || {}
|
26
26
|
pass_opts.delete(:defer)
|
27
|
-
traveled_path = @traveled_path[1
|
27
|
+
traveled_path = @traveled_path[1..] || []
|
28
28
|
if !traveled_path.empty?
|
29
29
|
pass_opts[:path_suffix] = traveled_path
|
30
30
|
end
|
@@ -36,8 +36,8 @@ module Props
|
|
36
36
|
yield
|
37
37
|
end
|
38
38
|
|
39
|
-
def set!(key, options={}, &block)
|
40
|
-
return if @found_block || !
|
39
|
+
def set!(key, options = {}, &block)
|
40
|
+
return if @found_block || !block
|
41
41
|
|
42
42
|
if @search_path[@depth] == key.to_s
|
43
43
|
@traveled_path.push(key)
|
@@ -64,7 +64,7 @@ module Props
|
|
64
64
|
return if @found_block
|
65
65
|
|
66
66
|
key_index = @search_path[@depth]
|
67
|
-
id_name, id_val = key_index.to_s.split(
|
67
|
+
id_name, id_val = key_index.to_s.split("=")
|
68
68
|
|
69
69
|
if id_val
|
70
70
|
id_val = id_val.to_i
|
@@ -80,7 +80,7 @@ module Props
|
|
80
80
|
|
81
81
|
if @depth == @search_path.size - 1
|
82
82
|
@found_options = pass_opts
|
83
|
-
@found_block =
|
83
|
+
@found_block = proc {
|
84
84
|
yield item, 0
|
85
85
|
}
|
86
86
|
return
|
data/lib/props_template.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
|
14
|
-
require
|
1
|
+
require "props_template/base"
|
2
|
+
require "props_template/extensions/partial_renderer"
|
3
|
+
require "props_template/extensions/cache"
|
4
|
+
require "props_template/extensions/deferment"
|
5
|
+
require "props_template/extensions/fragment"
|
6
|
+
require "props_template/base_with_extensions"
|
7
|
+
require "props_template/extension_manager"
|
8
|
+
require "active_support/core_ext/string/output_safety"
|
9
|
+
require "active_support/core_ext/array"
|
10
|
+
require "props_template/searcher"
|
11
|
+
require "props_template/handler"
|
12
|
+
require "props_template/version"
|
13
|
+
|
14
|
+
require "active_support"
|
15
15
|
|
16
16
|
module Props
|
17
17
|
class Template
|
@@ -19,7 +19,7 @@ module Props
|
|
19
19
|
attr_accessor :template_lookup_options
|
20
20
|
end
|
21
21
|
|
22
|
-
self.template_lookup_options = {
|
22
|
+
self.template_lookup_options = {handlers: [:props]}
|
23
23
|
|
24
24
|
delegate :result!, :array!,
|
25
25
|
:deferred!,
|
@@ -34,7 +34,7 @@ module Props
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def set!(key, options = {}, &block)
|
37
|
-
if
|
37
|
+
if block && options[:search] && !@builder.is_a?(Searcher)
|
38
38
|
|
39
39
|
prev_builder = @builder
|
40
40
|
@builder = Searcher.new(self, options[:search], @context)
|
@@ -60,4 +60,4 @@ module Props
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
require
|
63
|
+
require "props_template/railtie" if defined?(Rails)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: props_template
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.24.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johny Ho
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04
|
11
|
+
date: 2023-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -65,7 +65,6 @@ files:
|
|
65
65
|
- lib/props_template/base.rb
|
66
66
|
- lib/props_template/base_with_extensions.rb
|
67
67
|
- lib/props_template/core_ext.rb
|
68
|
-
- lib/props_template/debug_writer.rb
|
69
68
|
- lib/props_template/dependency_tracker.rb
|
70
69
|
- lib/props_template/extension_manager.rb
|
71
70
|
- lib/props_template/extensions/cache.rb
|
@@ -77,9 +76,6 @@ files:
|
|
77
76
|
- lib/props_template/railtie.rb
|
78
77
|
- lib/props_template/searcher.rb
|
79
78
|
- lib/props_template/version.rb
|
80
|
-
- spec/layout_spec.rb
|
81
|
-
- spec/props_template_spec.rb
|
82
|
-
- spec/searcher_spec.rb
|
83
79
|
homepage: https://github.com/thoughtbot/props_template/
|
84
80
|
licenses:
|
85
81
|
- MIT
|
@@ -103,7 +99,4 @@ rubygems_version: 3.1.6
|
|
103
99
|
signing_key:
|
104
100
|
specification_version: 4
|
105
101
|
summary: A fast JSON builder
|
106
|
-
test_files:
|
107
|
-
- spec/searcher_spec.rb
|
108
|
-
- spec/layout_spec.rb
|
109
|
-
- spec/props_template_spec.rb
|
102
|
+
test_files: []
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'oj'
|
2
|
-
require 'active_support'
|
3
|
-
require 'byebug'
|
4
|
-
|
5
|
-
module Props
|
6
|
-
class DebugWriter
|
7
|
-
attr_accessor :commands
|
8
|
-
|
9
|
-
def initialize(opts)
|
10
|
-
@stream = Oj::StringWriter.new(opts)
|
11
|
-
@commands = []
|
12
|
-
end
|
13
|
-
|
14
|
-
def push_object
|
15
|
-
@commands.push([:push_object])
|
16
|
-
end
|
17
|
-
|
18
|
-
def push_key(key)
|
19
|
-
@commands.push([:push_key, key])
|
20
|
-
end
|
21
|
-
|
22
|
-
def push_value(value, key = nil)
|
23
|
-
if key
|
24
|
-
@commands.push([:push_value, value, key])
|
25
|
-
else
|
26
|
-
@commands.push([:push_value, value])
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def push_array
|
31
|
-
@commands.push([:push_array])
|
32
|
-
end
|
33
|
-
|
34
|
-
def pop
|
35
|
-
@commands.push([:pop])
|
36
|
-
end
|
37
|
-
|
38
|
-
def reset
|
39
|
-
@commands = []
|
40
|
-
@stream.reset
|
41
|
-
end
|
42
|
-
|
43
|
-
def to_s
|
44
|
-
@commands.each do |command|
|
45
|
-
begin
|
46
|
-
@stream.send(*command)
|
47
|
-
rescue => e
|
48
|
-
byebug
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
@stream.to_s
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/spec/layout_spec.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require_relative "./support/helper"
|
2
|
-
require_relative "./support/rails_helper"
|
3
|
-
require "props_template/layout_patch"
|
4
|
-
require "action_controller"
|
5
|
-
|
6
|
-
RSpec.describe "Props::Template" do
|
7
|
-
class TestController < ActionController::Base
|
8
|
-
protect_from_forgery
|
9
|
-
|
10
|
-
def self.controller_path
|
11
|
-
""
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
it "uses a layout to render" do
|
16
|
-
view_path = File.join(File.dirname(__FILE__), "./fixtures")
|
17
|
-
controller = TestController.new
|
18
|
-
controller.prepend_view_path(view_path)
|
19
|
-
|
20
|
-
json = controller.render_to_string("200", layout: "application")
|
21
|
-
|
22
|
-
expect(json.strip).to eql('{"data":{"success":"ok"}}')
|
23
|
-
end
|
24
|
-
end
|
data/spec/props_template_spec.rb
DELETED
@@ -1,300 +0,0 @@
|
|
1
|
-
require_relative './support/helper'
|
2
|
-
|
3
|
-
RSpec.describe 'Props::Base' do
|
4
|
-
it 'initializes' do
|
5
|
-
expect {
|
6
|
-
Props::Base.new
|
7
|
-
}.to_not raise_error
|
8
|
-
end
|
9
|
-
|
10
|
-
context 'result!' do
|
11
|
-
it 'returns {} when empty' do
|
12
|
-
json = Props::Base.new
|
13
|
-
expect(json.result!.strip).to eql('{}')
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'resets OJ' do
|
17
|
-
json = Props::Base.new
|
18
|
-
json.set! :foo, 'bar'
|
19
|
-
attrs = json.result!.strip
|
20
|
-
|
21
|
-
expect(attrs).to eql_json({
|
22
|
-
foo: 'bar'
|
23
|
-
})
|
24
|
-
|
25
|
-
expect(json.result!.strip).to eql_json({})
|
26
|
-
|
27
|
-
json.set! :foo, 'bar'
|
28
|
-
attrs = json.result!.strip
|
29
|
-
expect(attrs).to eql_json({
|
30
|
-
foo: 'bar'
|
31
|
-
})
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'set!' do
|
36
|
-
it 'sets a value' do
|
37
|
-
json = Props::Base.new
|
38
|
-
json.set! :foo, 'bar'
|
39
|
-
attrs = json.result!.strip
|
40
|
-
|
41
|
-
expect(attrs).to eql_json({
|
42
|
-
foo: 'bar'
|
43
|
-
})
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'sets a empty obj when block is empty' do
|
47
|
-
json = Props::Base.new
|
48
|
-
json.set! :foo do
|
49
|
-
end
|
50
|
-
attrs = json.result!.strip
|
51
|
-
|
52
|
-
expect(attrs).to eql_json({
|
53
|
-
foo: {}
|
54
|
-
})
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'sets a empty obj when nested block is empty' do
|
58
|
-
json = Props::Base.new
|
59
|
-
json.set! :foo do
|
60
|
-
json.set! :bar do
|
61
|
-
end
|
62
|
-
end
|
63
|
-
attrs = json.result!.strip
|
64
|
-
|
65
|
-
expect(attrs).to eql_json({
|
66
|
-
foo: {
|
67
|
-
bar: {}
|
68
|
-
}
|
69
|
-
})
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'sets a null value' do
|
73
|
-
json = Props::Base.new
|
74
|
-
json.set! :foo, nil
|
75
|
-
attrs = json.result!.strip
|
76
|
-
|
77
|
-
expect(attrs).to eql_json({
|
78
|
-
foo: nil
|
79
|
-
})
|
80
|
-
end
|
81
|
-
|
82
|
-
it 'sets multiple values' do
|
83
|
-
json = Props::Base.new
|
84
|
-
json.set! :foo, 'bar'
|
85
|
-
json.set! :steve, 'cool'
|
86
|
-
|
87
|
-
attrs = json.result!.strip
|
88
|
-
|
89
|
-
expect(attrs).to eql_json({
|
90
|
-
foo: 'bar',
|
91
|
-
steve: 'cool'
|
92
|
-
})
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'sets multiple values with the same key, the last one wins' do
|
96
|
-
json = Props::Base.new
|
97
|
-
json.set! :foo, 'bar'
|
98
|
-
json.set! :foo, 'cool'
|
99
|
-
|
100
|
-
attrs = JSON.parse(json.result!)
|
101
|
-
|
102
|
-
expect(attrs).to eql({
|
103
|
-
'foo' => 'cool'
|
104
|
-
})
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'throws InvalidScopeForObjError when the current scope is an array' do
|
108
|
-
json = Props::Base.new
|
109
|
-
json.array! [1, 2] do |item|
|
110
|
-
json.set! :foo, item
|
111
|
-
end
|
112
|
-
expect {
|
113
|
-
json.set! :bar, 'world'
|
114
|
-
}.to raise_error(Props::InvalidScopeForObjError)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
context 'set! with a block' do
|
119
|
-
it 'creates a new nested object' do
|
120
|
-
json = Props::Base.new
|
121
|
-
json.set! :outer do
|
122
|
-
json.set! :inner, 'baz'
|
123
|
-
end
|
124
|
-
|
125
|
-
attrs = json.result!.strip
|
126
|
-
|
127
|
-
expect(attrs).to eql_json({
|
128
|
-
outer: {
|
129
|
-
inner: 'baz'
|
130
|
-
}
|
131
|
-
})
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'creates a nested object' do
|
135
|
-
json = Props::Base.new
|
136
|
-
json.set! :outer do
|
137
|
-
json.set! :inner, 'baz'
|
138
|
-
end
|
139
|
-
|
140
|
-
attrs = json.result!.strip
|
141
|
-
|
142
|
-
expect(attrs).to eql_json({
|
143
|
-
outer: {
|
144
|
-
inner: 'baz'
|
145
|
-
}
|
146
|
-
})
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'creates a nested array of objects' do
|
150
|
-
json = Props::Base.new
|
151
|
-
json.set! :outer do
|
152
|
-
json.array! [1, 2] do |item|
|
153
|
-
json.set! :foo, item
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
attrs = json.result!.strip
|
158
|
-
|
159
|
-
expect(attrs).to eql_json({
|
160
|
-
outer: [
|
161
|
-
{foo: 1},
|
162
|
-
{foo: 2}
|
163
|
-
]
|
164
|
-
})
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'treats the second argument as an options arg' do
|
168
|
-
json = Props::Base.new
|
169
|
-
json.set! :outer, {some: 'setting'} do
|
170
|
-
json.set! :inner, 'baz'
|
171
|
-
end
|
172
|
-
|
173
|
-
attrs = json.result!.strip
|
174
|
-
|
175
|
-
expect(attrs).to eql_json({
|
176
|
-
outer: {
|
177
|
-
inner: 'baz'
|
178
|
-
}
|
179
|
-
})
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
context 'array!' do
|
184
|
-
it 'creates an array of 1 object' do
|
185
|
-
json = Props::Base.new
|
186
|
-
json.array! [1] do |num|
|
187
|
-
json.set! :foo, num
|
188
|
-
end
|
189
|
-
|
190
|
-
attrs = json.result!.strip
|
191
|
-
|
192
|
-
expect(attrs).to eql_json([
|
193
|
-
{foo: 1}
|
194
|
-
])
|
195
|
-
end
|
196
|
-
|
197
|
-
it 'passes the index as the second argument of yield' do
|
198
|
-
json = Props::Base.new
|
199
|
-
json.array! ['a', 'b'] do |item, index|
|
200
|
-
json.set! :foo, [item, index]
|
201
|
-
end
|
202
|
-
|
203
|
-
attrs = json.result!.strip
|
204
|
-
|
205
|
-
expect(attrs).to eql_json([
|
206
|
-
{foo: ['a', 0]},
|
207
|
-
{foo: ['b', 1]}
|
208
|
-
])
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'creates an empty array when passed an empty collection' do
|
212
|
-
json = Props::Base.new
|
213
|
-
json.array! [] do |num|
|
214
|
-
end
|
215
|
-
|
216
|
-
attrs = json.result!.strip
|
217
|
-
|
218
|
-
expect(attrs).to eql_json([
|
219
|
-
])
|
220
|
-
end
|
221
|
-
|
222
|
-
it 'creates an array of empty arrays when passed an empty collection' do
|
223
|
-
json = Props::Base.new
|
224
|
-
json.array! [1] do |num|
|
225
|
-
json.array! [] do
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
attrs = json.result!.strip
|
230
|
-
expect(attrs).to eql_json([[]])
|
231
|
-
end
|
232
|
-
|
233
|
-
it 'creates an array of empties if set did not get called in array' do
|
234
|
-
json = Props::Base.new
|
235
|
-
json.array! [1, 2, 3] do |num|
|
236
|
-
end
|
237
|
-
|
238
|
-
attrs = json.result!.strip
|
239
|
-
|
240
|
-
expect(attrs).to eql_json([
|
241
|
-
{},
|
242
|
-
{},
|
243
|
-
{}
|
244
|
-
])
|
245
|
-
end
|
246
|
-
|
247
|
-
it 'throws InvalidScopeForArray error when array is used twice' do
|
248
|
-
json = Props::Base.new
|
249
|
-
json.array! [1] do
|
250
|
-
json.set! :foo, 'first'
|
251
|
-
end
|
252
|
-
|
253
|
-
expect {
|
254
|
-
json.array! [2] do |num|
|
255
|
-
json.set! :foo, 'second'
|
256
|
-
end
|
257
|
-
}.to raise_error(Props::InvalidScopeForArrayError)
|
258
|
-
end
|
259
|
-
|
260
|
-
it 'creates an array of multiple objects' do
|
261
|
-
json = Props::Base.new
|
262
|
-
json.array! [1, 2] do |num|
|
263
|
-
json.set! :foo, num
|
264
|
-
end
|
265
|
-
|
266
|
-
attrs = json.result!.strip
|
267
|
-
|
268
|
-
expect(attrs).to eql_json([
|
269
|
-
{foo: 1},
|
270
|
-
{foo: 2}
|
271
|
-
])
|
272
|
-
end
|
273
|
-
|
274
|
-
it 'creates an array of arrays' do
|
275
|
-
json = Props::Base.new
|
276
|
-
json.array! [[1, 2], [3, 4]] do |part|
|
277
|
-
json.array! part do |num|
|
278
|
-
json.set! :foo, num
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
attrs = json.result!.strip
|
283
|
-
|
284
|
-
expect(attrs).to eql_json([
|
285
|
-
[{foo: 1}, {foo: 2}],
|
286
|
-
[{foo: 3}, {foo: 4}]
|
287
|
-
])
|
288
|
-
end
|
289
|
-
|
290
|
-
it 'throws InvalidScopeForArrayError when the current scope is already an object' do
|
291
|
-
json = Props::Base.new
|
292
|
-
json.set! :foo, 'bar'
|
293
|
-
expect {
|
294
|
-
json.array! [1, 2] do |num|
|
295
|
-
json.set! :foo, 'noop'
|
296
|
-
end
|
297
|
-
}.to raise_error(Props::InvalidScopeForArrayError)
|
298
|
-
end
|
299
|
-
end
|
300
|
-
end
|
data/spec/searcher_spec.rb
DELETED
@@ -1,209 +0,0 @@
|
|
1
|
-
require_relative './support/helper'
|
2
|
-
|
3
|
-
RSpec.describe 'Searcher' do
|
4
|
-
it 'searching for a child node returns the proc, and refined found options' do
|
5
|
-
json = Props::Searcher.new(nil, ['outer', 'inner', 'hit'])
|
6
|
-
target_proc = Proc.new {}
|
7
|
-
target_opts = {some_options: 1}
|
8
|
-
json.set!('outer') do
|
9
|
-
json.set!('inner') do
|
10
|
-
json.set!('hit', target_opts, &target_proc)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
found_block, found_options = json.found!
|
15
|
-
expect(found_block).to eql(target_proc)
|
16
|
-
expect(found_options).to eql({
|
17
|
-
some_options: 1,
|
18
|
-
path_suffix: ['inner', 'hit']
|
19
|
-
})
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'searching with an empty path means you found nothing' do
|
23
|
-
json = Props::Searcher.new(nil, [])
|
24
|
-
|
25
|
-
target_proc = Proc.new do
|
26
|
-
json.set!('inner') do
|
27
|
-
json.set!('hit', {}, &target_proc)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
json.set!('outer', {}, &target_proc)
|
31
|
-
|
32
|
-
found_block, found_options = json.found!
|
33
|
-
expect(found_block).to be_nil
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'searching for a child node with siblings in back' do
|
37
|
-
json = Props::Searcher.new(nil, ['outer', 'inner'])
|
38
|
-
target_proc = Proc.new do
|
39
|
-
json.set!('foo', 32)
|
40
|
-
end
|
41
|
-
|
42
|
-
json.set!('outer') do
|
43
|
-
json.set!('inner', {}, &target_proc)
|
44
|
-
|
45
|
-
json.set!('bad') do
|
46
|
-
json.set!('foo', 'should not touch')
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
found_block, found_options = json.found!
|
51
|
-
expect(found_block).to eql(target_proc)
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'searching for a child node with siblings in front' do
|
55
|
-
json = Props::Searcher.new(nil, ['outer', 'inner'])
|
56
|
-
target_proc = Proc.new do
|
57
|
-
json.foo 32
|
58
|
-
end
|
59
|
-
|
60
|
-
json.set!('outer') do
|
61
|
-
json.set!('bad') do
|
62
|
-
json.set!('foo', 'should not touch')
|
63
|
-
end
|
64
|
-
|
65
|
-
json.set!('inner', {}, &target_proc)
|
66
|
-
end
|
67
|
-
|
68
|
-
found_block, found_options = json.found!
|
69
|
-
expect(found_block).to eql(target_proc)
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'searching for a subtree' do
|
73
|
-
json = Props::Searcher.new(nil, ['outer', 'inner', 'deep'])
|
74
|
-
|
75
|
-
target_proc = Proc.new do
|
76
|
-
json.set!('deeper') do
|
77
|
-
json.set!('foo', 32)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
json.set!('outer') do
|
82
|
-
json.set!('inner') do
|
83
|
-
json.set!('deep', {}, &target_proc)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
found_block, found_options = json.found!
|
88
|
-
expect(found_block).to eql(target_proc)
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'searching for a leaf node is unsupported' do
|
92
|
-
json = Props::Searcher.new(nil, ['outer', 'inner', 'foo'])
|
93
|
-
json.set!('outer') do
|
94
|
-
json.set!('inner') do
|
95
|
-
json.set!('foo', 32)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
found_block, found_options = json.found!
|
100
|
-
expect(found_block).to be_nil
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'searching for a node beyond whats available is equivalent to not finding anything' do
|
104
|
-
json = Props::Searcher.new(nil, ['outer', 'inner', 'a', 'b'])
|
105
|
-
json.set!('outer') do
|
106
|
-
json.set!('inner') do
|
107
|
-
json.set!('foo', 32)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
found_block, found_options = json.found!
|
112
|
-
expect(found_block).to be_nil
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'searching for an item inside an array, includes refined found options' do
|
116
|
-
json = Props::Searcher.new(nil, ['outer', 1])
|
117
|
-
target_opts = {some_options: 1}
|
118
|
-
json.set!('outer') do
|
119
|
-
json.array!(['hello', 'world'], target_opts) do |item|
|
120
|
-
item
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
found_block, found_options = json.found!
|
125
|
-
expect(found_options).to eql({some_options: 1, path_suffix: [1]})
|
126
|
-
expect(found_block.call).to eql('world')
|
127
|
-
end
|
128
|
-
|
129
|
-
it 'searching for an item inside an array using an id=val keypath' do
|
130
|
-
json = Props::Searcher.new(nil, ['outer', 'id=1'])
|
131
|
-
|
132
|
-
class Collection
|
133
|
-
def initialize(ary, rspec)
|
134
|
-
@ary = ary
|
135
|
-
@rspec = rspec
|
136
|
-
end
|
137
|
-
|
138
|
-
def member_by(key, value)
|
139
|
-
@rspec.expect(key).to @rspec.eql('id')
|
140
|
-
@rspec.expect(value).to @rspec.eql(1)
|
141
|
-
@ary.last
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
collection = Collection.new(
|
146
|
-
[{id: 0}, {id: 1}],
|
147
|
-
self
|
148
|
-
)
|
149
|
-
|
150
|
-
json.set!('outer') do
|
151
|
-
json.array!(collection) do |item|
|
152
|
-
item
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
found_block, found_options = json.found!
|
157
|
-
expect(found_block.call).to eql({id: 1})
|
158
|
-
end
|
159
|
-
|
160
|
-
it 'searching for an node belonging an array' do
|
161
|
-
json = Props::Searcher.new(nil, ['outer', 'inner', 1, 'foo'])
|
162
|
-
json.set!('outer') do
|
163
|
-
json.set!('inner') do
|
164
|
-
json.array! ['hello', 'world'] do |item|
|
165
|
-
json.set!('foo') do
|
166
|
-
item
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
found_block, found_options = json.found!
|
172
|
-
expect(found_block.call).to eql('world')
|
173
|
-
end
|
174
|
-
|
175
|
-
it 'searching for an node outside the length of the array, is equivalent to not finding anything' do
|
176
|
-
json = Props::Searcher.new(nil, ['outer','inner', 10, 'foo'])
|
177
|
-
json.set!('outer') do
|
178
|
-
json.set!('inner') do
|
179
|
-
json.array! [1, 2] do |item|
|
180
|
-
json.set!('foo') do
|
181
|
-
json.set!('bar', item)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
found_block, found_options = json.found!
|
188
|
-
expect(found_block).to be_nil
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'searching for object inside a nested array' do
|
192
|
-
json = Props::Searcher.new(nil, ['outer', 'inner', 1, 'foo', 0])
|
193
|
-
json.set!('outer') do
|
194
|
-
json.set!('inner') do
|
195
|
-
json.array! [0, 1] do |item|
|
196
|
-
json.set!('foo') do
|
197
|
-
json.array! ['hello', 'world'] do |inner_item|
|
198
|
-
inner_item
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
found_block, found_options = json.found!
|
206
|
-
expect(found_block.call).to eql('hello')
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|