Skip to content
Browse files

[builder,engine] Add conditional support for attribute (Fix #354)

  • Loading branch information...
1 parent 6faba95 commit e8b312aee6d5488878d90e3cd9894031171395f2 @nesquena committed Nov 10, 2012
Showing with 67 additions and 12 deletions.
  1. +1 −0 CHANGELOG.md
  2. +7 −0 README.md
  3. +1 −0 lib/rabl.rb
  4. +6 −4 lib/rabl/builder.rb
  5. +5 −3 lib/rabl/engine.rb
  6. +5 −5 test/builder_test.rb
  7. +42 −0 test/engine_test.rb
View
1 CHANGELOG.md
@@ -4,6 +4,7 @@
* Fix #344 to avoid: "warning: default `to_a' will be obsolete"
* Fix #356 by adding 'known object classes' like struct to be recognized as objects.
+ * Fix #354 by adding 'if' and 'unless' to `attribute` (Thanks @andrewhubbs)
## 0.7.6
View
7 README.md
@@ -253,6 +253,13 @@ attributes :bar => :baz, :dog => :animal
# => # { baz : <bar value>, animal : <dog value> }
```
+or show attributes only if a condition is true:
+
+```ruby
+# m is the object being rendered, also supports :unless
+attributes :foo, :bar, :if => lambda { |m| m.condition? }
+```
+
Named and aliased attributes can not be combined on the same line. This currently does not work:
```ruby
View
1 lib/rabl.rb
@@ -2,6 +2,7 @@
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/reverse_merge'
+require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'
require 'rabl/version'
View
10 lib/rabl/builder.rb
@@ -34,8 +34,8 @@ def compile_hash(options={})
extends(settings[:file], settings[:options], &settings[:block])
end if @options.has_key?(:extends)
# Attributes
- @options[:attributes].each_pair do |attribute, name|
- attribute(attribute, :as => name)
+ @options[:attributes].each_pair do |attribute, options|

Unfortunately this leads to a subtle and hard to track error: object root nodes go missing. The reason is variable shadowing of options as method parameter and as block variable (only applies to ruby 1.8).

Once found, the fix is easy: sebastianludwig@ee2dbc2

@ohadlevy
ohadlevy added a note Nov 18, 2012

:+1: I've encountered the same problem

@nesquena
Owner
nesquena added a note Nov 18, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ attribute(attribute, options)
end if @options.has_key?(:attributes)
# Node
@options[:node].each do |settings|
@@ -63,14 +63,16 @@ def compile_hash(options={})
# Indicates an attribute or method should be included in the json output
# attribute :foo, :as => "bar"
+ # attribute :foo, :as => "bar", :if => lambda { |m| m.foo }
def attribute(name, options={})
- @_result[options[:as] || name] = data_object_attribute(name) if @_object && @_object.respond_to?(name)
+ if @_object && @_object.respond_to?(name) && resolve_condition(options)
+ @_result[options[:as] || name] = data_object_attribute(name)
+ end
end
alias_method :attributes, :attribute
# Creates an arbitrary node that is included in the json output
# node(:foo) { "bar" }
- # node(:foo) { "bar" }
# node(:foo, :if => lambda { |m| m.foo.present? }) { "bar" }
def node(name, options={}, &block)
return unless resolve_condition(options)
View
8 lib/rabl/engine.rb
@@ -152,13 +152,15 @@ def cache(key = nil, options = nil)
# Indicates an attribute or method should be included in the json output
# attribute :foo, :as => "bar"
- # attribute :foo => :bar
+ # attribute :foo => :bar, :bar => :baz
+ # attribute :foo => :bar, :bar => :baz, :if => lambda { |r| r.foo }
def attribute(*args)
if args.first.is_a?(Hash) # :foo => :bar, :bar => :baz
- args.first.each_pair { |k,v| self.attribute(k, :as => v) }
+ attr_aliases, conds = args.first.except(:if, :unless), args.first.slice(:if, :unless)
+ attr_aliases.each_pair { |k,v| self.attribute(k, conds.merge(:as => v)) }
else # array of attributes i.e :foo, :bar, :baz
options = args.extract_options!
- args.each { |name| @_options[:attributes][name] = options[:as] || name }
+ args.each { |name| @_options[:attributes][name] = options }
end
end
alias_method :attributes, :attribute
View
10 test/builder_test.rb
@@ -23,21 +23,21 @@
context "#to_hash" do
context "when given a simple object" do
- setup { builder({ :attributes => { :name => :name } }) }
+ setup { builder({ :attributes => { :name => {} } }) }
asserts "that the object is set properly" do
topic.build(User.new, :root_name => "user")
end.equivalent_to({ "user" => { :name => "rabl" } })
end
context "when given an object alias" do
- setup { builder({ :attributes => { :name => :name } }) }
+ setup { builder({ :attributes => { :name => { :as => :foo } } }) }
asserts "that the object is set properly" do
topic.build(User.new, :root_name => "person")
- end.equivalent_to({ "person" => { :name => "rabl" } })
+ end.equivalent_to({ "person" => { :foo => "rabl" } })
end
context "when specified with no root" do
- setup { builder({ :attributes => { :name => :name } }) }
+ setup { builder({ :attributes => { :name => { :as => :name } } }) }
asserts "that the object is set properly" do
topic.build(User.new, :root => false)
end.equivalent_to({ :name => "rabl" })
@@ -46,7 +46,7 @@
context "#attribute" do
asserts "that the node" do
- build_hash @user, :attributes => { :name => :name, :city => :city }
+ build_hash @user, :attributes => { :name => {}, :city => { :as => :city } }
end.equivalent_to({:name => 'rabl', :city => 'irvine'})
asserts "that with a non-existent attribute the node" do
View
42 test/engine_test.rb
@@ -440,6 +440,48 @@
scope.instance_variable_set :@user, User.new(:city => City.new('San Francisco'))
template.render(scope)
end.equals "{\"city\":{\"name\":\"San Francisco\"}}"
+
+ asserts "that it can be passed an if cond for single real attr" do
+ template = rabl %{
+ object @user
+ attribute :name
+ attributes :age, :first, :if => lambda { |i| i.name != 'irvine' }
+ }
+ scope = Object.new
+ scope.instance_variable_set :@user, User.new(:name => 'irvine')
+ JSON.parse(template.render(scope))
+ end.equals JSON.parse("{\"name\":\"irvine\"}")
+
+ asserts "that it can be passed an if cond for aliased attrs" do
+ template = rabl %{
+ object @user
+ attributes :name => :title, :age => :year, :if => lambda { |i| i.name == 'irvine' }
+ }
+ scope = Object.new
+ scope.instance_variable_set :@user, User.new(:name => 'irvine')
+ JSON.parse(template.render(scope))
+ end.equals JSON.parse("{\"title\":\"irvine\",\"year\":24}")
+
+ asserts "that it can be passed an unless cond to hide attrs" do
+ template = rabl %{
+ object @user
+ attribute :name
+ attributes :age, :unless => lambda { |i| i.name == 'irvine' }
+ }
+ scope = Object.new
+ scope.instance_variable_set :@user, User.new(:name => 'irvine')
+ JSON.parse(template.render(scope))
+ end.equals JSON.parse("{\"name\":\"irvine\"}")
+
+ asserts "that it can be passed an unless cond for aliased attrs" do
+ template = rabl %{
+ object @user
+ attributes :name => :title, :age => :year, :unless => lambda { |i| i.name == 'irvine' }
+ }
+ scope = Object.new
+ scope.instance_variable_set :@user, User.new(:name => 'irvine')
+ JSON.parse(template.render(scope))
+ end.equals JSON.parse("{}")
end # attribute
context "#code" do

0 comments on commit e8b312a

Please sign in to comment.
Something went wrong with that request. Please try again.