props_template 0.13.0 → 0.17.1

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.
@@ -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,11 +13,9 @@ 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!,
22
- :fragment_digest!,
23
19
  to: :builder!
24
20
 
25
21
  def initialize(context = nil, options = {})
@@ -49,11 +45,8 @@ module Props
49
45
  @builder
50
46
  end
51
47
 
52
- private
53
-
54
- def method_missing(*args, &block)
55
- set!(*args, &block)
56
- end
48
+ alias_method :method_missing, :set!
49
+ private :method_missing
57
50
  end
58
51
  end
59
52
 
@@ -1,6 +1,5 @@
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
@@ -8,38 +7,49 @@ module Props
8
7
  class InvalidScopeForObjError < StandardError; end
9
8
 
10
9
  class Base
11
- def initialize
12
- @commands = [[:push_object]]
10
+ def initialize(encoder = nil)
13
11
  @stream = Oj::StringWriter.new(mode: :rails)
14
12
  @scope = nil
15
13
  end
16
14
 
17
15
  def set_block_content!(options = {})
18
- @commands.push([:push_object])
19
16
  @scope = nil
20
17
  yield
21
- @commands.push([:pop])
18
+ if @scope.nil?
19
+ @stream.push_object
20
+ end
21
+ @stream.pop
22
22
  end
23
23
 
24
24
  def handle_set_block(key, options)
25
- @commands.push([:push_key, key.to_s])
25
+ @stream.push_key(key)
26
26
  set_block_content!(options) do
27
27
  yield
28
28
  end
29
29
  end
30
30
 
31
+ def format_key(key)
32
+ key.to_s
33
+ end
34
+
31
35
  def set!(key, value = nil)
32
- @scope ||= :object
36
+ key = format_key(key)
37
+
33
38
  if @scope == :array
34
39
  raise InvalidScopeForObjError.new('Attempted to set! on an array! scope')
35
40
  end
41
+
42
+ if @scope.nil?
43
+ @scope = :object
44
+ @stream.push_object
45
+ end
46
+
36
47
  if block_given?
37
48
  handle_set_block(key, value) do
38
49
  yield
39
50
  end
40
51
  else
41
- @commands.push([:push_key, key.to_s])
42
- @commands.push([:push_value, value])
52
+ @stream.push_value(value, key)
43
53
  end
44
54
 
45
55
  @scope = :object
@@ -76,19 +86,18 @@ module Props
76
86
  end
77
87
  end
78
88
  end
89
+
79
90
  #todo, add ability to define contents of array
80
91
  def array!(collection, options = {})
81
92
  if @scope.nil?
82
93
  @scope = :array
94
+ @stream.push_array
83
95
  else
84
96
  raise InvalidScopeForArrayError.new('array! expects exclusive use of this block')
85
97
  end
86
- @commands[-1] = [:push_array]
87
98
 
88
- if !collection.empty?
89
- handle_collection(collection, options) do |item, index|
90
- yield item, index
91
- end
99
+ handle_collection(collection, options) do |item, index|
100
+ yield item, index
92
101
  end
93
102
 
94
103
  @scope = :array
@@ -96,18 +105,18 @@ module Props
96
105
  nil
97
106
  end
98
107
 
99
- def commands_to_json!(commands)
100
- commands.each do |command|
101
- @stream.send(*command)
108
+ def result!
109
+ if @scope.nil?
110
+ @stream.push_object
111
+ @stream.pop
112
+ else
113
+ @stream.pop
102
114
  end
103
- json = @stream.to_s
104
- @stream.reset
105
- json
106
- end
107
115
 
108
- def result!
109
- @commands.push([:pop])
110
- commands_to_json!(@commands)
116
+ json = @stream.raw_json
117
+ @scope = nil
118
+ @key_cache = {}
119
+ json
111
120
  end
112
121
  end
113
122
  end
@@ -3,13 +3,12 @@ require 'props_template/extensions/partial_renderer'
3
3
  require 'props_template/extensions/cache'
4
4
  require 'props_template/extensions/deferment'
5
5
  require 'props_template/extension_manager'
6
- require 'props_template/key_formatter'
7
-
8
6
  require 'active_support/core_ext/string/output_safety'
7
+ require 'active_support/core_ext/array'
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
@@ -17,7 +16,7 @@ module Props
17
16
  #todo: refactor so deferred can be its own class
18
17
  @em = ExtensionManager.new(self)
19
18
  @traveled_path = []
20
- @key_formatter = KeyFormatter.new(camelize: :lower)
19
+ @key_cache = {}
21
20
  super()
22
21
  end
23
22
 
@@ -29,33 +28,32 @@ module Props
29
28
  @em.fragments
30
29
  end
31
30
 
32
- def fragment_digest!
33
- @em.fragment_digest
34
- end
35
-
36
31
  def set_block_content!(options = {})
37
32
  return super if !@em.has_extensions(options)
38
33
 
39
- @em.handle(@commands, options) do
34
+ @em.handle(options) do
40
35
  yield
41
36
  end
42
37
  end
43
38
 
44
39
  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]
40
+ prev_state = [@stream, @em.deferred, @em.fragments]
41
+ @em = ExtensionManager.new(self)
42
+ prev_scope = @scope
43
+ @scope = nil
44
+
45
+ yield @stream, @em.deferred, @em.fragments
46
+
47
+ @scope = prev_scope
51
48
  @em = ExtensionManager.new(self, prev_state[1], prev_state[2])
49
+ end
52
50
 
53
- next_state
51
+ def format_key(key)
52
+ @key_cache[key] ||= key.to_s.camelize(:lower)
53
+ @key_cache[key].dup
54
54
  end
55
55
 
56
56
  def set!(key, options = {}, &block)
57
- key = @key_formatter.format(key)
58
-
59
57
  if block_given?
60
58
  options = @em.refine_options(options)
61
59
  end
@@ -78,23 +76,15 @@ module Props
78
76
  end
79
77
 
80
78
  def handle_collection_item(collection, item, index, options)
81
- if collection.respond_to? :member_key
82
- member_key = collection.member_key
83
- end
84
-
85
- if !member_key
79
+ if !options[:key]
86
80
  @traveled_path.push(index)
87
81
  else
88
- id = if item.respond_to? member_key
89
- item.send(member_key)
90
- elsif item.is_a? Hash
91
- item[member_key] || item[member_key.to_sym]
92
- end
82
+ id, val = options[:key]
93
83
 
94
84
  if id.nil?
95
85
  @traveled_path.push(index)
96
86
  else
97
- @traveled_path.push("#{member_key.to_s}=#{id}")
87
+ @traveled_path.push("#{id.to_s}=#{val}")
98
88
  end
99
89
  end
100
90
 
@@ -108,8 +98,19 @@ module Props
108
98
  @em.refine_all_item_options(all_options)
109
99
  end
110
100
 
111
-
112
101
  def refine_item_options(item, options)
102
+ return options if options.empty?
103
+
104
+ if key = options[:key]
105
+ val = if item.respond_to? key
106
+ item.send(key)
107
+ elsif item.is_a? Hash
108
+ item[key] || item[key.to_sym]
109
+ end
110
+
111
+ options[:key] = [options[:key], val]
112
+ end
113
+
113
114
  @em.refine_options(options, item)
114
115
  end
115
116
  end
@@ -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
@@ -7,7 +7,7 @@ module Props
7
7
  class ExtensionManager
8
8
  attr_reader :base, :builder, :context
9
9
 
10
- def initialize(base, defered=[], fragments={})
10
+ def initialize(base, defered=[], fragments=[])
11
11
  @base = base
12
12
  @context = base.context
13
13
  @builder = base.builder
@@ -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
@@ -34,36 +36,35 @@ module Props
34
36
  @deferment.deferred
35
37
  end
36
38
 
37
- def fragment_digest
38
- @fragment.name
39
- end
40
-
41
39
  def fragments
42
40
  @fragment.fragments
43
41
  end
44
42
 
45
43
  def has_extensions(options)
46
- options[:defer] || options[:cache] || options[:partial]
44
+ options[:defer] || options[:cache] || options[:partial] || options[:key]
47
45
  end
48
46
 
49
- def handle(commands, options)
47
+ def handle(options)
50
48
  return yield if !has_extensions(options)
51
49
 
52
50
  if options[:defer]
53
51
  placeholder = @deferment.handle(options)
54
- commands.push([:push_value, placeholder])
52
+ base.stream.push_value(placeholder)
55
53
  @fragment.handle(options)
56
54
  else
57
55
  handle_cache(options) do
58
56
  base.set_block_content! do
59
57
  if options[:partial]
60
- current_digest = @fragment.name
61
58
  @fragment.handle(options)
62
59
  @partialer.handle(options)
63
- @fragment.name = current_digest
64
60
  else
65
61
  yield
66
62
  end
63
+
64
+ if options[:key]
65
+ id, val = options[:key]
66
+ base.set!(id, val)
67
+ end
67
68
  end
68
69
  end
69
70
  end
@@ -71,33 +72,33 @@ module Props
71
72
 
72
73
  private
73
74
 
74
- def content_for_cache(commands, deferred_paths, fragment_paths)
75
- suffix = [
76
- [:push_value, deferred_paths],
77
- [:push_value, fragment_paths],
78
- [:pop]
79
- ]
80
-
81
- [[:push_array]] + commands + suffix
82
- end
83
-
84
75
  def handle_cache(options)
85
76
  if options[:cache]
77
+ recently_cached = false
78
+
86
79
  state = @cache.cache(*options[:cache]) do
87
- commands = content_for_cache(*base.scoped_state {yield})
88
- base.commands_to_json!(commands).strip
80
+ recently_cached = true
81
+ result = nil
82
+ start = base.stream.to_s.length
83
+ base.scoped_state { |stream, deferred_paths, fragment_paths|
84
+ yield
85
+ meta = Oj.dump([deferred_paths, fragment_paths]).strip
86
+ json_in_progress = base.stream.to_s
87
+ if json_in_progress[start] == ','
88
+ start += 1
89
+ end
90
+ raw = base.stream.to_s[start..-1].strip
91
+ result = "#{meta}\n#{raw}"
92
+ }
93
+ result
89
94
  end
90
95
 
91
- value, next_deferred, next_fragments = Oj.load(state)
92
- base.commands.push([:push_value, value])
93
- deferred.push(*next_deferred)
94
-
95
- next_fragments.each do |k, v|
96
- if fragments[k]
97
- fragments[k].push(*v)
98
- else
99
- fragments[k] = v
100
- end
96
+ if !recently_cached
97
+ meta, raw_json = state.split("\n")
98
+ next_deferred, next_fragments = Oj.load(meta)
99
+ base.stream.push_json(raw_json)
100
+ deferred.push(*next_deferred)
101
+ fragments.push(*next_fragments)
101
102
  end
102
103
  else
103
104
  yield
@@ -25,12 +25,6 @@ module Props
25
25
  @context
26
26
  end
27
27
 
28
- def instrument(name, **options)
29
- ActiveSupport::Notifications.instrument(name, options) do |payload|
30
- yield payload
31
- end
32
- end
33
-
34
28
  def multi_fetch(keys, options = {})
35
29
  result = {}
36
30
  key_to_ckey = {}
@@ -49,7 +43,7 @@ module Props
49
43
 
50
44
  read_caches = {}
51
45
 
52
- instrument('read_multi_fragments.action_view', payload) do |payload|
46
+ ActiveSupport::Notifications.instrument('read_multi_fragments.action_view', payload) do |payload|
53
47
  read_caches = ::Rails.cache.read_multi(*ckeys, options)
54
48
  payload[:read_caches] = read_caches
55
49
  end
@@ -122,7 +116,7 @@ module Props
122
116
 
123
117
  def cache_key(key, options)
124
118
  name_options = options.slice(:skip_digest, :virtual_path)
125
- key = fragment_name_with_digest(key, name_options)
119
+ key = @context.cache_fragment_name(key, **name_options)
126
120
 
127
121
  if @context.respond_to?(:combined_fragment_cache_key)
128
122
  key = @context.combined_fragment_cache_key(key)
@@ -132,19 +126,6 @@ module Props
132
126
 
133
127
  ::ActiveSupport::Cache.expand_cache_key(key, :props)
134
128
  end
135
-
136
- def fragment_name_with_digest(key, options)
137
- if @context.respond_to?(:cache_fragment_name)
138
- # Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
139
- # should be used instead.
140
- @context.cache_fragment_name(key, options)
141
- elsif @context.respond_to?(:fragment_name_with_digest)
142
- # Backwards compatibility for period of time when fragment_name_with_digest was made public.
143
- @context.fragment_name_with_digest(key)
144
- else
145
- key
146
- end
147
- end
148
129
  end
149
130
  end
150
131