From 92de606ead527040975e43e8ff19e30038ace617 Mon Sep 17 00:00:00 2001 From: jho406 Date: Tue, 31 Dec 2024 11:42:00 -0500 Subject: [PATCH] Alias dig to search. (#40) Search is a bit of a confusing way to describe what traversing is. Dig seems more appropriate and is consistent with how superglue describes it. --- README.md | 26 +++---- lib/props_template.rb | 8 +- spec/extensions/prop_template_search_spec.rb | 82 ++++++++++++-------- 3 files changed, 70 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 2f90c1c..3ec60b1 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ gem 'props_template' and run `bundle`. Optionally add the [core ext](#array-core-extension) to an initializer if you -want to [dig](#traversing) into your templates. +want to [dig](#digging) into your templates. ```ruby require 'props_template/core_ext' @@ -152,7 +152,7 @@ The difference between the block form and inline form is 1. The block form is an internal node. Functionality such as Partials, Deferment and other [options](#options) are only available on the block form. - 2. The inline form is considered a leaf node, and you can only [search](#traversing) + 2. The inline form is considered a leaf node, and you can only [dig](#digging) for internal nodes. ### json.extract! @@ -202,7 +202,7 @@ end | collection | A collection that responds to `member_at` and `member_by` | | options | Additional [options](#options)| -To support [traversing nodes](#traversing), any list passed +To support [digging](#digging), any list passed to `array!` MUST implement `member_at(index)` and `member_by(attr, value)`. For example, if you were using a delegate: @@ -276,7 +276,7 @@ end ``` PropsTemplate does not know what the elements are in your collection. The -example above will be fine for [traversing](#traversing) +example above will be fine for [digging](#digging) by index, but will raise a `NotImplementedError` if you query by attribute. You may still need to implement `member_by`. @@ -447,7 +447,7 @@ entirely and replace the value with a placeholder. A common use case would be tabbed content that does not load until you click the tab. When your client receives the payload, you may issue a second request to the -same endpoint to fetch any missing nodes. See [traversing nodes](#traversing) +same endpoint to fetch any missing nodes. See [digging](#digging) There is also an `defer: :auto` option that you can use with [SuperglueJS][1]. [SuperglueJS][1] will use the metadata from `json.deferred!` to issue a `remote` dispatch to fetch @@ -502,7 +502,7 @@ your collection item, and is used for `defer: :auto` to generate a keypath for [SuperglueJS][1]. If you are NOT using SuperglueJS, you do not need to do this. 2. Implement `member_at`, on the [collection](#jsonarray). This will be called -by PropsTemplate to when [searching nodes](#traversing) +by PropsTemplate to when [digging](#digging) For example: @@ -528,7 +528,7 @@ end If you are using [SuperglueJS][1], SuperglueJS will, it will automatically kick off `remote(?props_at=posts.some_id=1.contact)` and `remote(?props_at=posts.some_id=2.contact)`. -## Traversing +## Digging PropsTemplate has the ability to walk the tree you build, skipping execution of untargeted nodes. This feature is useful for selectively updating your frontend @@ -537,7 +537,7 @@ state. ```ruby traversal_path = ['data', 'details', 'personal'] -json.data(search: traversal_path) do +json.data(dig: traversal_path) do json.details do json.employment do ...more stuff @@ -571,13 +571,13 @@ The above will output: } ``` -Searching only works with blocks, and will NOT work with Scalars +Digging only works with blocks, and will NOT work with Scalars ("leaf" values). For example: ```ruby traversal_path = ['data', 'details', 'personal', 'name'] <- not found -json.data(search: traversal_path) do +json.data(dig: traversal_path) do json.details do json.personal do json.name 'james' @@ -588,12 +588,12 @@ end ## Nodes that do not exist -Nodes that are not found will remove the branch where search was enabled on. +Nodes that are not found will remove the branch where digging was enabled on. ```ruby traversal_path = ['data', 'details', 'does_not_exist'] -json.data(search: traversal_path) do +json.data(dig: traversal_path) do json.details do json.personal do json.name 'james' @@ -642,7 +642,7 @@ will render Layout first, then the template when `yield json` is used. ## Change key format By default, keys are not formatted. This is intentional. By being explicity with your keys, -it makes your views quicker and more easily searchable when working in Javascript land. +it makes your views quicker and more easily diggable when working in Javascript land. If you must change this behavior, override it in an initializer and cache the value: diff --git a/lib/props_template.rb b/lib/props_template.rb index 9016637..adf7531 100644 --- a/lib/props_template.rb +++ b/lib/props_template.rb @@ -36,11 +36,15 @@ def initialize(context = nil, options = {}) end def set!(key, options = {}, &block) - if block && options[:search] && !@builder.is_a?(Searcher) + if block && (options[:search] || options[:dig]) && !@builder.is_a?(Searcher) + search = options[:search] || options[:dig] prev_builder = @builder - @builder = Searcher.new(self, options[:search], @context) + @builder = Searcher.new(self, search, @context) + options.delete(:search) + options.delete(:dig) + @builder.set!(key, options, &block) found_block, found_options = @builder.found! @builder = prev_builder diff --git a/spec/extensions/prop_template_search_spec.rb b/spec/extensions/prop_template_search_spec.rb index cbf17a6..07f8e23 100644 --- a/spec/extensions/prop_template_search_spec.rb +++ b/spec/extensions/prop_template_search_spec.rb @@ -7,6 +7,26 @@ end it "finds the correct node and merges it to the top" do + json = render(<<~PROPS) + json.data(dig: ['data', 'comment', 'details']) do + json.comment do + json.details do + json.name 'john' + end + end + end + json.foo 'bar' + PROPS + + expect(json).to eql_json({ + data: { + name: "john" + }, + foo: "bar" + }) + end + + it "aliases dig to search and finds the correct node and merges it to the top" do json = render(<<~PROPS) json.data(search: ['data', 'comment', 'details']) do json.comment do @@ -28,7 +48,7 @@ it "finds an empty child node and returns an empty object" do json = render(<<~PROPS) - json.data(search: ['data', 'inner']) do + json.data(dig: ['data', 'inner']) do json.inner do end end @@ -43,7 +63,7 @@ it "searching for a non-existant child does not set the parent, simulating undefined in JS" do json = render(<<~PROPS) - json.data(search: ['data', 'does_not_exist']) do + json.data(dig: ['data', 'does_not_exist']) do json.inner do end end @@ -57,7 +77,7 @@ it "searching for nil does nothing" do json = render(<<~PROPS) - json.data(search: nil) do + json.data(dig: nil) do json.inner do end end @@ -74,7 +94,7 @@ it "searching for an empty array means we found nothing" do json = render(<<~PROPS) - json.data(search: []) do + json.data(dig: []) do json.inner do end end @@ -88,7 +108,7 @@ it "searching for a child node with siblings in back" do json = render(<<~PROPS) - json.outer(search: ['outer', 'inner']) do + json.outer(dig: ['outer', 'inner']) do json.inner do json.foo 32 end @@ -109,7 +129,7 @@ it "searching for a child node with siblings in front" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner']) do + json.outer(dig: ['outer', 'inner']) do json.bad do raise 'this should not happen' json.foo 'should not touch' @@ -130,13 +150,13 @@ it "searches on multiple siblings" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner']) do + json.outer(dig: ['outer', 'inner']) do json.inner do json.foo 32 end end - json.first(search: ['first', 'second']) do + json.first(dig: ['first', 'second']) do json.second do json.bar 'cool' end @@ -155,8 +175,8 @@ it "reenables search functionality at the contents of found obj nodes" do json = render(<<-PROPS) - json.outer(search: ['outer']) do - json.inner(search: ['inner', 'foo']) do + json.outer(dig: ['outer']) do + json.inner(dig: ['inner', 'foo']) do json.foo do json.firstName 'john' end @@ -175,8 +195,8 @@ it "ignores search functionality in between levels of traversal" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner', 'foo']) do - json.inner(search: ['does_not_exist']) do + json.outer(dig: ['outer', 'inner', 'foo']) do + json.inner(dig: ['does_not_exist']) do json.foo do json.firstName 'john' end @@ -193,7 +213,7 @@ it "find the correct node in a partial" do json = render(<<~PROPS) - json.data(search: ['data', 'comment', 'details']) do + json.data(dig: ['data', 'comment', 'details']) do json.comment(partial: 'comment') do end end @@ -208,7 +228,7 @@ it "finds a subtree" do json = render(<<-PROPS) - json.outer(search: ['outer','inner', 'deep']) do + json.outer(dig: ['outer','inner', 'deep']) do json.inner do json.deep do json.deeper do @@ -230,7 +250,7 @@ it "searching for a leaf node is unsupported" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner', 'foo']) do + json.outer(dig: ['outer', 'inner', 'foo']) do json.inner do json.foo 32 end @@ -245,7 +265,7 @@ it "searching for a node beyond whats available is equivalent to not finding anything" do json = render(<<-PROPS) - json.outer(search: ['inner', 'a', 'b']) do + json.outer(dig: ['inner', 'a', 'b']) do json.inner do json.foo 32 end @@ -260,7 +280,7 @@ it "finds an array" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner']) do + json.outer(dig: ['outer', 'inner']) do json.inner do json.array! [1, 2] do |item| json.foo item @@ -279,7 +299,7 @@ it "searching for an item inside an array" do json = render(<<-PROPS) - json.outer(search: ['outer', 0]) do + json.outer(dig: ['outer', 0]) do json.array! ['hello', 'world'] do |item| json.foo item end @@ -295,7 +315,7 @@ it "searching for an node beyond an array" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner', 1, 'foo']) do + json.outer(dig: ['outer', 'inner', 1, 'foo']) do json.inner do json.array! [1, 2] do |item| json.foo do @@ -315,7 +335,7 @@ it "searching for an node outside the length of the array, is equivalent to not finding anything" do json = render(<<-PROPS) - json.outer(search: ['inner', 10, 'foo']) do + json.outer(dig: ['inner', 10, 'foo']) do json.inner do json.array! [1, 2] do |item| json.foo do @@ -334,7 +354,7 @@ it "searching for an node outside the length of the array, is equivalent to not finding anything" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner', 10, 'foo']) do + json.outer(dig: ['outer', 'inner', 10, 'foo']) do json.inner do json.array! [1, 2] do |item| json.foo do @@ -353,7 +373,7 @@ it "searching for nested array" do json = render(<<-PROPS) - json.outer(search: ['outer', 'inner', 1, 'foo']) do + json.outer(dig: ['outer', 'inner', 1, 'foo']) do json.inner do json.array! [0, 1] do |item| json.foo do @@ -376,7 +396,7 @@ it "searching for object inside a nested array" do json = render(<<-PROPS) - json.outer(search: ['outer','inner', 1, 'foo', 0]) do + json.outer(dig: ['outer','inner', 1, 'foo', 0]) do json.inner do json.array! [0, 1] do |item| json.foo do @@ -397,7 +417,7 @@ context "when searching through a partial" do it "returns the correct node in an object" do json = render(<<~PROPS) - json.data(search: ['data', 'comment', 'details']) do + json.data(dig: ['data', 'comment', 'details']) do json.comment(partial: 'comment') do end end @@ -412,7 +432,7 @@ it "ignores the fragment option" do json = render(<<~PROPS) - json.data(search: ['data', 'comment', 'details']) do + json.data(dig: ['data', 'comment', 'details']) do json.comment(partial: ['comment', fragment: 'foobar']) do end end @@ -429,7 +449,7 @@ it "passes the found child obj options back to the parent" do json = render(<<~PROPS) - json.data(search: ['data', 'comment']) do + json.data(dig: ['data', 'comment']) do json.comment(partial: 'comment') do end end @@ -447,7 +467,7 @@ it "passes the found child obj in array options back to the parent" do json = render(<<~PROPS) - json.data(search: ['data', 'comment', 1]) do + json.data(dig: ['data', 'comment', 1]) do json.comment do json.array! ['hello', 'world'], {partial: ['profile', as: :email]} do end @@ -464,7 +484,7 @@ it "returns the correct node in an array" do json = render(<<~PROPS) - json.data(search: ['data', 'comment', 0]) do + json.data(dig: ['data', 'comment', 0]) do json.comment do json.array! [0], {partial: 'simple'} do end @@ -481,7 +501,7 @@ it "returns the correct node beyond an array" do json = render(<<~PROPS) - json.data(search: ['data','comment', 0, 'details']) do + json.data(dig: ['data','comment', 0, 'details']) do json.comment do json.array! [0], {partial: 'comment'} do end @@ -498,7 +518,7 @@ it "returns the correct node across nested partials" do json = render(<<~PROPS) - json.data(search: ['data', 'comment', 'details', 'contact', 'phone']) do + json.data(dig: ['data', 'comment', 'details', 'contact', 'phone']) do json.comment(partial: 'complex') do end end @@ -514,7 +534,7 @@ it "returns the correct node across nested partials" do json = render(<<~PROPS) - json.data(search: ['data', 'comments', 0, 0, 'details', 'contact', 'phone']) do + json.data(dig: ['data', 'comments', 0, 0, 'details', 'contact', 'phone']) do opts = { partial: ['complex_children', locals: {children: [1,2]}] }