props_template 0.14.0 → 0.15.0

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