props_template 0.14.0 → 0.15.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 -1
- data/lib/props_template.rb +2 -8
- data/lib/props_template/base.rb +40 -23
- data/lib/props_template/base_with_extensions.rb +12 -12
- data/lib/props_template/debug_writer.rb +55 -0
- data/lib/props_template/extension_manager.rb +31 -22
- data/lib/props_template/railtie.rb +0 -8
- data/spec/props_template_spec.rb +4 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f324de590ac7fad6c1152e6e45a80b407044461fb33fc6b265955afd536d2aca
|
4
|
+
data.tar.gz: 945bd9ffa5457a422d65e49fdc16d5f73c1718fad4e38a78923afee24f7fa067
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c4b2f13494e4ec00f5c2a9510f536d3bff51133b36726dc4ed4aaef2f03940a314fbdd5163406e0826535df45268c7e3b4300e4f3fda8536da5b64a3c3346b8
|
7
|
+
data.tar.gz: 0ce796eb28ce86a5d4fb00301c72ac2b97fd5a500fba43cb32c6d3ab3df2c5c36a628973d6fd9975dca24fdbdb8ea0f2754eaea23f28e4664d65a3d22c4b5097
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# PropsTemplate
|
2
2
|
|
3
|
-
PropsTemplate is a queryable JSON templating library inspired by JBuilder. It has support for layouts, partials, russian-doll caching, multi-fetch and can selectively render nodes in your tree without executing others.
|
3
|
+
PropsTemplate is a fast queryable JSON templating library inspired by JBuilder. It has support for layouts, partials, russian-doll caching, multi-fetch and can selectively render nodes in your tree without executing others.
|
4
4
|
|
5
5
|
Example:
|
6
6
|
|
@@ -45,6 +45,29 @@ json.footer partial: 'shared/footer' do
|
|
45
45
|
end
|
46
46
|
```
|
47
47
|
|
48
|
+
## Installation
|
49
|
+
If you plan to use PropsTemplate alone just add it to your Gemfile.
|
50
|
+
|
51
|
+
```
|
52
|
+
gem 'props_template'
|
53
|
+
```
|
54
|
+
|
55
|
+
and run `bundle`
|
56
|
+
|
57
|
+
Then add the following to a initializer:
|
58
|
+
|
59
|
+
```
|
60
|
+
Props.reset_encoder!
|
61
|
+
```
|
62
|
+
|
63
|
+
PropsTemplate uses a single instance of `Oj::StringWriter` for a process. If you're using a forking server like puma, be sure to add this to your `config/puma.rb`.
|
64
|
+
|
65
|
+
```
|
66
|
+
on_worker_boot do
|
67
|
+
Props.reset_encoder!
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
48
71
|
## API
|
49
72
|
|
50
73
|
### json.set! or json.<your key here>
|
data/lib/props_template.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
require 'props_template/base_with_extensions'
|
4
2
|
require 'props_template/searcher'
|
5
3
|
require 'props_template/handler'
|
@@ -15,7 +13,6 @@ module Props
|
|
15
13
|
self.template_lookup_options = { handlers: [:props] }
|
16
14
|
|
17
15
|
delegate :result!, :array!,
|
18
|
-
:commands_to_json!,
|
19
16
|
:deferred!,
|
20
17
|
:fragments!,
|
21
18
|
:set_block_content!,
|
@@ -49,11 +46,8 @@ module Props
|
|
49
46
|
@builder
|
50
47
|
end
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
def method_missing(*args, &block)
|
55
|
-
set!(*args, &block)
|
56
|
-
end
|
49
|
+
alias_method :method_missing, :set!
|
50
|
+
private :method_missing
|
57
51
|
end
|
58
52
|
end
|
59
53
|
|
data/lib/props_template/base.rb
CHANGED
@@ -1,45 +1,62 @@
|
|
1
1
|
require 'oj'
|
2
2
|
require 'active_support'
|
3
|
-
|
4
3
|
#todo: active_support/core_ext/string/output_safety.rb
|
5
4
|
|
6
5
|
module Props
|
7
6
|
class InvalidScopeForArrayError < StandardError; end
|
8
7
|
class InvalidScopeForObjError < StandardError; end
|
9
8
|
|
9
|
+
def self.reset_encoder!
|
10
|
+
@@encoder = Oj::StringWriter.new(mode: :rails)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.encoder
|
14
|
+
@@encoder
|
15
|
+
end
|
16
|
+
|
10
17
|
class Base
|
11
|
-
def initialize
|
12
|
-
@
|
13
|
-
@stream
|
18
|
+
def initialize(encoder = nil)
|
19
|
+
@stream = encoder || Props.encoder
|
20
|
+
@stream.reset
|
14
21
|
@scope = nil
|
22
|
+
@key_cache = {}
|
15
23
|
end
|
16
24
|
|
17
25
|
def set_block_content!(options = {})
|
18
|
-
@commands.push([:push_object])
|
19
26
|
@scope = nil
|
20
27
|
yield
|
21
|
-
@
|
28
|
+
if @scope.nil?
|
29
|
+
@stream.push_object
|
30
|
+
end
|
31
|
+
@stream.pop
|
22
32
|
end
|
23
33
|
|
24
34
|
def handle_set_block(key, options)
|
25
|
-
@
|
35
|
+
@stream.push_key(key)
|
26
36
|
set_block_content!(options) do
|
27
37
|
yield
|
28
38
|
end
|
29
39
|
end
|
30
40
|
|
31
41
|
def set!(key, value = nil)
|
32
|
-
@
|
42
|
+
@key_cache[key] ||= key.to_s.freeze
|
43
|
+
key = @key_cache[key]
|
44
|
+
|
33
45
|
if @scope == :array
|
34
46
|
raise InvalidScopeForObjError.new('Attempted to set! on an array! scope')
|
35
47
|
end
|
48
|
+
|
49
|
+
if @scope.nil?
|
50
|
+
@scope = :object
|
51
|
+
@stream.push_object
|
52
|
+
end
|
53
|
+
|
36
54
|
if block_given?
|
37
55
|
handle_set_block(key, value) do
|
38
56
|
yield
|
39
57
|
end
|
40
58
|
else
|
41
|
-
@
|
42
|
-
@commands.push([:push_value, value])
|
59
|
+
@stream.push_value(value, key)
|
43
60
|
end
|
44
61
|
|
45
62
|
@scope = :object
|
@@ -76,19 +93,18 @@ module Props
|
|
76
93
|
end
|
77
94
|
end
|
78
95
|
end
|
96
|
+
|
79
97
|
#todo, add ability to define contents of array
|
80
98
|
def array!(collection, options = {})
|
81
99
|
if @scope.nil?
|
82
100
|
@scope = :array
|
101
|
+
@stream.push_array
|
83
102
|
else
|
84
103
|
raise InvalidScopeForArrayError.new('array! expects exclusive use of this block')
|
85
104
|
end
|
86
|
-
@commands[-1] = [:push_array]
|
87
105
|
|
88
|
-
|
89
|
-
|
90
|
-
yield item, index
|
91
|
-
end
|
106
|
+
handle_collection(collection, options) do |item, index|
|
107
|
+
yield item, index
|
92
108
|
end
|
93
109
|
|
94
110
|
@scope = :array
|
@@ -96,18 +112,19 @@ module Props
|
|
96
112
|
nil
|
97
113
|
end
|
98
114
|
|
99
|
-
def
|
100
|
-
|
101
|
-
@stream.
|
115
|
+
def result!
|
116
|
+
if @scope.nil?
|
117
|
+
@stream.push_object
|
118
|
+
@stream.pop
|
119
|
+
else
|
120
|
+
@stream.pop
|
102
121
|
end
|
122
|
+
|
103
123
|
json = @stream.to_s
|
124
|
+
@scope = nil
|
125
|
+
@key_cache = {}
|
104
126
|
@stream.reset
|
105
127
|
json
|
106
128
|
end
|
107
|
-
|
108
|
-
def result!
|
109
|
-
@commands.push([:pop])
|
110
|
-
commands_to_json!(@commands)
|
111
|
-
end
|
112
129
|
end
|
113
130
|
end
|
@@ -4,12 +4,11 @@ require 'props_template/extensions/cache'
|
|
4
4
|
require 'props_template/extensions/deferment'
|
5
5
|
require 'props_template/extension_manager'
|
6
6
|
require 'props_template/key_formatter'
|
7
|
-
|
8
7
|
require 'active_support/core_ext/string/output_safety'
|
9
8
|
|
10
9
|
module Props
|
11
10
|
class BaseWithExtensions < Base
|
12
|
-
attr_reader :builder, :context, :fragments, :traveled_path, :deferred, :
|
11
|
+
attr_reader :builder, :context, :fragments, :traveled_path, :deferred, :stream
|
13
12
|
|
14
13
|
def initialize(builder, context = nil, options = {})
|
15
14
|
@context = context
|
@@ -36,21 +35,21 @@ module Props
|
|
36
35
|
def set_block_content!(options = {})
|
37
36
|
return super if !@em.has_extensions(options)
|
38
37
|
|
39
|
-
@em.handle(
|
38
|
+
@em.handle(options) do
|
40
39
|
yield
|
41
40
|
end
|
42
41
|
end
|
43
42
|
|
44
43
|
def scoped_state
|
45
|
-
prev_state = [@
|
46
|
-
@
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@
|
51
|
-
@em = ExtensionManager.new(self, prev_state[1], prev_state[2])
|
44
|
+
prev_state = [@stream, @em.deferred, @em.fragments]
|
45
|
+
@em = ExtensionManager.new(self)
|
46
|
+
prev_scope = @scope
|
47
|
+
@scope = nil
|
48
|
+
|
49
|
+
yield @stream, @em.deferred, @em.fragments
|
52
50
|
|
53
|
-
|
51
|
+
@scope = prev_scope
|
52
|
+
@em = ExtensionManager.new(self, prev_state[1], prev_state[2])
|
54
53
|
end
|
55
54
|
|
56
55
|
def set!(key, options = {}, &block)
|
@@ -100,8 +99,9 @@ module Props
|
|
100
99
|
@em.refine_all_item_options(all_options)
|
101
100
|
end
|
102
101
|
|
103
|
-
|
104
102
|
def refine_item_options(item, options)
|
103
|
+
return options if options.empty?
|
104
|
+
|
105
105
|
if key = options[:key]
|
106
106
|
val = if item.respond_to? key
|
107
107
|
item.send(key)
|
@@ -0,0 +1,55 @@
|
|
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
|
@@ -25,6 +25,8 @@ module Props
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def refine_all_item_options(all_options)
|
28
|
+
return all_options if all_options.empty?
|
29
|
+
|
28
30
|
all_options = @partialer.find_and_add_template(all_options)
|
29
31
|
all_options = @cache.multi_fetch_and_add_results(all_options)
|
30
32
|
all_options
|
@@ -46,12 +48,12 @@ module Props
|
|
46
48
|
options[:defer] || options[:cache] || options[:partial] || options[:key]
|
47
49
|
end
|
48
50
|
|
49
|
-
def handle(
|
51
|
+
def handle(options)
|
50
52
|
return yield if !has_extensions(options)
|
51
53
|
|
52
54
|
if options[:defer]
|
53
55
|
placeholder = @deferment.handle(options)
|
54
|
-
|
56
|
+
base.stream.push_value(placeholder)
|
55
57
|
@fragment.handle(options)
|
56
58
|
else
|
57
59
|
handle_cache(options) do
|
@@ -76,32 +78,39 @@ module Props
|
|
76
78
|
|
77
79
|
private
|
78
80
|
|
79
|
-
def content_for_cache(commands, deferred_paths, fragment_paths)
|
80
|
-
suffix = [
|
81
|
-
[:push_value, deferred_paths],
|
82
|
-
[:push_value, fragment_paths],
|
83
|
-
[:pop]
|
84
|
-
]
|
85
|
-
|
86
|
-
[[:push_array]] + commands + suffix
|
87
|
-
end
|
88
|
-
|
89
81
|
def handle_cache(options)
|
90
82
|
if options[:cache]
|
83
|
+
recently_cached = false
|
84
|
+
|
91
85
|
state = @cache.cache(*options[:cache]) do
|
92
|
-
|
93
|
-
|
86
|
+
recently_cached = true
|
87
|
+
result = nil
|
88
|
+
start = base.stream.to_s.length
|
89
|
+
base.scoped_state { |stream, deferred_paths, fragment_paths|
|
90
|
+
yield
|
91
|
+
meta = Oj.dump([deferred_paths, fragment_paths]).strip
|
92
|
+
json_in_progress = base.stream.to_s
|
93
|
+
if json_in_progress[start] == ','
|
94
|
+
start += 1
|
95
|
+
end
|
96
|
+
raw = base.stream.to_s[start..-1].strip
|
97
|
+
result = "#{meta}\n#{raw}"
|
98
|
+
}
|
99
|
+
result
|
94
100
|
end
|
95
101
|
|
96
|
-
|
97
|
-
|
98
|
-
|
102
|
+
if !recently_cached
|
103
|
+
meta, raw_json = state.split("\n")
|
104
|
+
next_deferred, next_fragments = Oj.load(meta)
|
105
|
+
base.stream.push_json(raw_json)
|
106
|
+
deferred.push(*next_deferred)
|
99
107
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
108
|
+
next_fragments.each do |k, v|
|
109
|
+
if fragments[k]
|
110
|
+
fragments[k].push(*v)
|
111
|
+
else
|
112
|
+
fragments[k] = v
|
113
|
+
end
|
105
114
|
end
|
106
115
|
end
|
107
116
|
else
|
@@ -10,13 +10,5 @@ module Props
|
|
10
10
|
require 'props_template/layout_patch'
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
14
|
-
# if Rails::VERSION::MAJOR >= 4
|
15
|
-
# generators do |app|
|
16
|
-
# Rails::Generators.configure! app.config.generators
|
17
|
-
# Rails::Generators.hidden_namespaces.uniq!
|
18
|
-
# require 'generators/rails/scaffold_controller_generator'
|
19
|
-
# end
|
20
|
-
# end
|
21
13
|
end
|
22
14
|
end
|
data/spec/props_template_spec.rb
CHANGED
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.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johny Ho
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- lib/props_template/base.rb
|
78
78
|
- lib/props_template/base_with_extensions.rb
|
79
79
|
- lib/props_template/core_ext.rb
|
80
|
+
- lib/props_template/debug_writer.rb
|
80
81
|
- lib/props_template/dependency_tracker.rb
|
81
82
|
- lib/props_template/extension_manager.rb
|
82
83
|
- lib/props_template/extensions/cache.rb
|
@@ -95,7 +96,7 @@ homepage: https://github.com/jho406/breezy/
|
|
95
96
|
licenses:
|
96
97
|
- MIT
|
97
98
|
metadata: {}
|
98
|
-
post_install_message:
|
99
|
+
post_install_message:
|
99
100
|
rdoc_options: []
|
100
101
|
require_paths:
|
101
102
|
- lib
|
@@ -111,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
112
|
version: '0'
|
112
113
|
requirements: []
|
113
114
|
rubygems_version: 3.0.3
|
114
|
-
signing_key:
|
115
|
+
signing_key:
|
115
116
|
specification_version: 4
|
116
117
|
summary: A JSON builder for your React props
|
117
118
|
test_files:
|