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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1775b28ee0009735bc63ccb79aa1b22ad397c07682699ac384568ccec3704be
4
- data.tar.gz: 286f275535b658c80a2f5e8bb965a7cdeb21ef559ecfb7096447180422d4e1fd
3
+ metadata.gz: ed3fa0a5fda4993c730d2391de3e2a1d7f05e43580dc5e429734b5f8e7b07391
4
+ data.tar.gz: 005ed8fa15532e3e80530fae2a4c1bbd0002d9b9dcf5e2a41c64d34c62df2db0
5
5
  SHA512:
6
- metadata.gz: ac6ac10322e53936d8535f0a89c82d4dac4894932f4ac2739bc1f68f1faddea2a7ca81d7c65bbf04dccd49991ce9c26908676157b42e5ade62dee7faed89aa22
7
- data.tar.gz: 5227b5b577af671e582bfc3e27b64c9e45c90bec592238833fbb7312cb54b370db748c6ed502d4389179b7d617cf31789c00719330958a4c95435f9b3824233a
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](https://github.com/malomalo/turbostreamer) for the
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/
@@ -1,9 +1,9 @@
1
- require 'oj'
2
- require 'active_support'
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('Attempted to set! on an array! scope')
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('array! expects exclusive use of this block')
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 block_given?
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
- return
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.to_s}=#{val}")
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
- return
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
-
@@ -7,7 +7,7 @@ module Props
7
7
  end
8
8
 
9
9
  def member_by(attribute, value)
10
- raise NotImplementedError, 'Implement member_by(attr, value) in your own delegate'
10
+ raise NotImplementedError, "Implement member_by(attr, value) in your own delegate"
11
11
  end
12
12
  end
13
13
  end
@@ -1,16 +1,15 @@
1
1
  # This was taken from jbuilder
2
- require 'props_template'
3
-
2
+ require "props_template"
4
3
 
5
4
  dependency_tracker = false
6
5
 
7
6
  begin
8
- require 'action_view'
9
- require 'action_view/dependency_tracker'
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 'cache_digests'
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
- options = Cache.refine_options(options, item)
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
- all_options = @cache.multi_fetch_and_add_results(all_options)
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..-1].strip
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
- def context
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('read_multi_fragments.action_view', payload) do |payload|
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 = opts[:cache][0]
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
- value = cache_fragment_for(key, options) do
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
- else
124
- key = url_for(key).split('://', 2).last if ::Hash === key
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] == 'props_at' }
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
- { type: fragment_name, partial: partial_name, path: path }
21
+ {type: fragment_name, partial: partial_name, path: path}
22
22
  )
23
23
  end
24
24
  end
@@ -1,4 +1,4 @@
1
- require 'action_view'
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?(?/) ? [] : @context.lookup_context.prefixes
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 % (partial.inspect))
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 % (as))
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 % (path))
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 'active_support'
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
- %{__already_defined = defined?(json); json||=Props::Template.new(self); #{source};
12
- json.result! unless (__already_defined && __already_defined != "method")
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 render(context, options)
4
- options[:locals] ||= {}
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(context, template, options[:layout], options[:locals])
6
+ render_props_template(view, template, layout_name, locals)
13
7
  else
14
- super(context, options)
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 = resolve_props_layout(path, layout_locals.keys, [formats.first])
24
- body = layout.render(view, layout_locals) do |json|
25
- locals[:json] = json
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 'rails/railtie'
2
- require 'props_template'
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 'props_template/dependency_tracker'
10
- require 'props_template/layout_patch'
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..-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 || !block_given?
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 = Proc.new {
83
+ @found_block = proc {
84
84
  yield item, 0
85
85
  }
86
86
  return
@@ -1,3 +1,3 @@
1
1
  module Props
2
- VERSION = "0.22.4".freeze
2
+ VERSION = "0.24.0".freeze
3
3
  end
@@ -1,17 +1,17 @@
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'
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 = { handlers: [:props] }
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 block_given? && options[:search] && !@builder.is_a?(Searcher)
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 'props_template/railtie' if defined?(Rails)
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.22.4
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-14 00:00:00.000000000 Z
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
@@ -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
@@ -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
-