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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad98bb076e592658c137fd423771b211760ba047a1f6f394efa7a531a25eedc5
4
- data.tar.gz: f5495c4eca8b7015ac5d18714a10a16735769128fac62b5693824b1ef0c6e505
3
+ metadata.gz: f324de590ac7fad6c1152e6e45a80b407044461fb33fc6b265955afd536d2aca
4
+ data.tar.gz: 945bd9ffa5457a422d65e49fdc16d5f73c1718fad4e38a78923afee24f7fa067
5
5
  SHA512:
6
- metadata.gz: 629b8dc24b25dc596ee6f2bb9d03abce69e5af292121d118a83e3d53b83f38ef7ccc155e4cc79d4469a6e588c2efa3231ac02b05447ed11aa6c4642fca833d6f
7
- data.tar.gz: 83d0aed77e8cba7cf1730307f15a6fed2b972a181a1722c0dbba85a03489c12ca8b47f89b515e48f8a776564f3c35b58c6118be9396e0eb270c48cfbe21794c9
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>
@@ -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
- private
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
 
@@ -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
- @commands = [[:push_object]]
13
- @stream = Oj::StringWriter.new(mode: :rails)
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
- @commands.push([:pop])
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
- @commands.push([:push_key, key.to_s])
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
- @scope ||= :object
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
- @commands.push([:push_key, key.to_s])
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
- if !collection.empty?
89
- handle_collection(collection, options) do |item, index|
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 commands_to_json!(commands)
100
- commands.each do |command|
101
- @stream.send(*command)
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, :commands
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(@commands, options) do
38
+ @em.handle(options) do
40
39
  yield
41
40
  end
42
41
  end
43
42
 
44
43
  def scoped_state
45
- prev_state = [@commands, @em.deferred, @em.fragments]
46
- @commands = []
47
- @em = ExtensionManager.new(self)
48
- yield
49
- next_state = [@commands, @em.deferred, @em.fragments]
50
- @commands = prev_state[0]
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
- next_state
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(commands, options)
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
- commands.push([:push_value, placeholder])
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
- commands = content_for_cache(*base.scoped_state {yield})
93
- base.commands_to_json!(commands).strip
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
- value, next_deferred, next_fragments = Oj.load(state)
97
- base.commands.push([:push_value, value])
98
- deferred.push(*next_deferred)
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
- next_fragments.each do |k, v|
101
- if fragments[k]
102
- fragments[k].push(*v)
103
- else
104
- fragments[k] = v
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
@@ -1,6 +1,10 @@
1
1
  require_relative './support/helper'
2
2
 
3
3
  RSpec.describe 'Props::Base' do
4
+ before do
5
+ Props.reset_encoder!
6
+ end
7
+
4
8
  it 'initializes' do
5
9
  expect {
6
10
  Props::Base.new
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.14.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-06-05 00:00:00.000000000 Z
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: