Skip to content

Commit

Permalink
Merge branch 'main' into feature/partial_api
Browse files Browse the repository at this point in the history
  • Loading branch information
kortirso authored Aug 3, 2024
2 parents 228d5bd + e1cd215 commit e0ade30
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 5 deletions.
15 changes: 15 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.

# More details are here: https://help.github.com/articles/about-codeowners/

# The '*' pattern is global owners.

# Order is important. The last matching pattern has the most precedence.
# The folders are ordered as follows:

# In each subsection folders are ordered first by depth, then alphabetically.
# This should make it easy to add new rules without breaking existing ones.

# Global rule:
* @jho406
82 changes: 77 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ You can also add a [layout](#layouts).

### json.set! or json.\<your key here\>

Defines the attribute or structure. All keys are automatically camelized lower
by default. See [Change Key Format](#change-key-format) to change this behavior.
Defines the attribute or structure. All keys are not formatted by default. See [Change Key Format](#change-key-format) to change this behavior.

```ruby
json.set! :authorDetails, {...options} do
Expand Down Expand Up @@ -156,6 +155,33 @@ The difference between the block form and inline form is
2. The inline form is considered a leaf node, and you can only [search](#traversing)
for internal nodes.

### json.extract!
Extracts attributes from object or hash in 1 line

```ruby
# without extract!
json.id user.id
json.email user.email
json.firstName user.first_name

# with extract!
json.extract! user, :id, :email, :first_name

# => {"id" => 1, "email" => "[email protected]", "first_name" => "user"}

# with extract! with key transformation
json.extract! user, :id, [:first_name, :firstName], [:last_name, :lastName]

# => {"id" => 1, "firstName" => "user", "lastName" => "last"}
```

The inline form defines object and attributes

| Parameter | Notes |
| :--- | :--- |
| object | An object |
| attributes | A list of attributes |

### json.array!
Generates an array of json objects.

Expand Down Expand Up @@ -291,7 +317,7 @@ end
### Partials

Partials are supported. The following will render the file
`views/posts/_blog_posts.json.props`, and set a local variable `foo` assigned
`views/posts/_blog_posts.json.props`, and set a local variable `post` assigned
with @post, which you can use inside the partial.

```ruby
Expand All @@ -303,6 +329,7 @@ Usage with arrays:

```ruby
# The `as:` option is supported when using `array!`
# Without `as:` option you can use blog_post variable (name is based on partial's name) inside partial

json.posts do
json.array! @posts, partial: ["posts/blog_post", locals: {foo: 'bar'}, as: 'post'] do
Expand Down Expand Up @@ -592,15 +619,60 @@ json.flash flash.to_h
will render Layout first, then the template when `yield json` is used.

## Change key format
By default, keys are not formatted. If you want to change this behavior,
override it in an initializer:
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.

If you must change this behavior, override it in an initializer and cache the value:

```ruby
# default behavior
Props::BaseWithExtensions.class_eval do
# json.firstValue "first"
# json.second_value "second"
#
# -> { "firstValue" => "first", "second_value" => "second" }
def key_format(key)
key.to_s
end
end

# camelCased behavior
Props::BaseWithExtensions.class_eval do
# json.firstValue "first"
# json.second_value "second"
#
# -> { "firstValue" => "first", "secondValue" => "second" }
def key_format(key)
@key_cache ||= {}
@key_cache[key] ||= key.to_s.camelize(:lower)
@key_cache[key]
end

def result!
result = super
@key_cache = {}
result
end
end

# snake_cased behavior
Props::BaseWithExtensions.class_eval do
# json.firstValue "first"
# json.second_value "second"
#
# -> { "first_value" => "first", "second_value" => "second" }
def key_format(key)
@key_cache ||= {}
@key_cache[key] ||= key.to_s.underscore
@key_cache[key]
end

def result!
result = super
@key_cache = {}
result
end
end
```

## Escape mode
Expand Down
1 change: 1 addition & 0 deletions lib/props_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class << self

delegate :result!, :array!,
:partial!,
:extract!,
:deferred!,
:fragments!,
:set_block_content!,
Expand Down
22 changes: 22 additions & 0 deletions lib/props_template/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,28 @@ def partial!(**options)
@context.render options
end

# json.id item.id
# json.value item.value
#
# json.extract! item, :id, :value
#
# with key transformation
# json.extract! item, :id, [:first_name, :firstName]
def extract!(object, *values)
values.each do |value|
key, attribute = if value.is_a?(Array)
[value[1], value[0]]
else
[value, value]
end

set!(
key,
object.is_a?(Hash) ? object.fetch(attribute) : object.public_send(attribute)
)
end
end

def result!
if @scope.nil?
@stream.push_object
Expand Down
49 changes: 49 additions & 0 deletions spec/props_template_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -356,4 +356,53 @@
])
end
end

context "extract!" do
it "extracts values for hash" do
object = { :foo => "bar", "bar" => "foo", :wiz => "wiz" }

json = Props::Base.new
json.extract! object, :foo, "bar"
attrs = json.result!.strip

expect(attrs).to eql_json({
foo: "bar",
bar: "foo"
})
end

it "extracts values for hash with key transformation" do
object = { :foo => "bar", "bar_bar" => "foo", :wiz => "wiz" }

json = Props::Base.new
json.extract! object, :foo, ["bar_bar", "barBar"]
attrs = json.result!.strip

expect(attrs).to eql_json({
foo: "bar",
barBar: "foo"
})
end

it "extracts values for object" do
class FooBar
def foo
"bar"
end

def bar
"foo"
end
end

json = Props::Base.new
json.extract! FooBar.new, :foo, "bar"
attrs = json.result!.strip

expect(attrs).to eql_json({
foo: "bar",
bar: "foo"
})
end
end
end

0 comments on commit e0ade30

Please sign in to comment.