Skip to content

Loading…

Deferred defaults for Dash #34

Merged
merged 1 commit into from

5 participants

@bradgessler

Its desirable to have Procs that populate a default value for a key in Dash. Consider a key that generates a GUID and/or populates created_at may be wanted for an object:

class DeferredTest < Hashie::Dash
  property :created_at, :default => Proc.new{ Time.now }
  property :guid, :default => Proc.new{ rand(10000) } # Crappy GUID, I know...
end

This pull request adds support for lazily evaluated defaults to Dash to support this use case.

@ChristianPeters

+1 Please merge this Pull Request!

Another important use case is (lazy) dynamic method invocation:

MyDash.property :width, default: proc { default_width }

def default_width
  # ...
end
@xavierdutreilh

+1 deferred defaults would be an awesome feature for Dash.

@pdf

Would most definitely like this merged...

@jch jch commented on an outdated diff
lib/hashie/dash.rb
@@ -108,8 +108,15 @@ def initialize(attributes = {}, &block)
def [](property)
assert_property_exists! property
value = super(property.to_s)
- yield value if block_given?
- value
+ # If the value is a lambda, proc, or whatever answers to call, eval the thing!
+ if value.respond_to? :call
@jch
jch added a note

How do you feel about changing this to value.is_a? Proc? It's unlikely that users will assign an object that responds to call accidentally, but it'd be very unexpected behavior. A contrived example would be defaulting a middleware class as a value. In console, it looks like Proc.new, proc {...} and lambda {...} all return things that are Proc objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jch jch commented on an outdated diff
spec/hashie/dash_spec.rb
@@ -22,6 +22,10 @@ class Subclassed < DashTest
property :last_name, :required => true
end
+class DeferredTest < Hashie::Dash
+ property :created_at, :default => Proc.new{ Time.now }
@jch
jch added a note

add a space between new and { please ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jch

Thanks for building out this useful feature! I'm going through an cleaning out issues and pulls and would love to merge this. Could you look over the comments I made and see if they're reasonable?

@bradgessler

Fixed everything per everybody's comments.

@jch jch merged commit 2cb0865 into intridea:master

1 check passed

Details default The Travis build passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 13, 2013
  1. @bradgessler

    Added support for deferred properties to Dash

    Brad Gessler committed with bradgessler
Showing with 22 additions and 2 deletions.
  1. +7 −2 lib/hashie/dash.rb
  2. +15 −0 spec/hashie/dash_spec.rb
View
9 lib/hashie/dash.rb
@@ -110,8 +110,13 @@ def initialize(attributes = {}, &block)
def [](property)
assert_property_exists! property
value = super(property.to_s)
- yield value if block_given?
- value
+ # If the value is a lambda, proc, or whatever answers to call, eval the thing!
+ if value.is_a? Proc
+ self[property] = value.call # Set the result of the call as a value
+ else
+ yield value if block_given?
+ value
+ end
end
# Set a value on the Dash in a Hash-like way. Only works
View
15 spec/hashie/dash_spec.rb
@@ -26,6 +26,10 @@ class DashDefaultTest < Hashie::Dash
property :aliases, :default => ["Snake"]
end
+class DeferredTest < Hashie::Dash
+ property :created_at, :default => Proc.new { Time.now }
+end
+
describe DashTest do
subject { DashTest.new(:first_name => 'Bob', :email => 'bob@example.com') }
@@ -100,6 +104,17 @@ class DashDefaultTest < Hashie::Dash
end
end
+ context 'reading from deferred properties' do
+ it 'should evaluate proc after initial read' do
+ DeferredTest.new['created_at'].should be_instance_of(Time)
+ end
+
+ it "should not evalute proc after subsequent reads" do
+ deferred = DeferredTest.new
+ deferred['created_at'].object_id.should == deferred['created_at'].object_id
+ end
+ end
+
describe '.new' do
it 'fails with non-existent properties' do
lambda { described_class.new(:bork => '') }.should raise_error(NoMethodError)
Something went wrong with that request. Please try again.