props_template 0.22.4 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|