Skip to content

Commit

Permalink
Addresses issue
Browse files Browse the repository at this point in the history
Addresses issue #113. Allows specification of a proc, block, or sym for default values.
  • Loading branch information
sxross committed Apr 22, 2014
1 parent 0a0ff3a commit 9c9ee6a
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 2 deletions.
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,62 @@ class Task
end
```

A note on defaults, you can specify a proc, block or symbol for your default if you want to get fancy. The most obvious use case for this is that Ruby will optimize the assignment of an array so that a default of `[]` always points to the same object. Not exactly what is intended. Wrapping this in a proc causes a new array to be created. Here's an example:

```
class Foo
include MotionModel::Model
include MotionModel::ArrayModelAdapter
columns subject: { type: :array, default: ->{ [] } }
end
```

This is not constrained to initializing arrays. You can
initialize pretty much anything using a proc or block.
If you are specifying a block, make sure to use begin/end
instead of do/end because it makes Ruby happy.

Here's a different example:

```
class Timely
include MotionModel::Model
include MotionModel::ArrayModelAdapter
columns ended_run_at: { type: :time, default: ->{ Time.now } }
end
```
Note that this uses the "stubby proc" syntax. That is pretty much equivalent
to:

```
columns ended_run_at: { type: :time, default: lambda { Time.now } }
```

for the previous example.

If you want to use a block, use the begin/end syntax:

```
columns ended_run_at: { type: :time, default:
begin
Time.now
end
}
```
Finally, you can have the default call some class method as follows:

```
class Timely
include MotionModel::Model
include MotionModel::ArrayModelAdapter
columns unique_thingie: { type: :integer, default: :randomize }
def self.randomize
rand 1_000_000
end
end
```

You can also include the `Validatable` module to get field validation. For example:

```ruby
Expand Down
34 changes: 33 additions & 1 deletion motion/model/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -785,8 +785,40 @@ def rebuild_relation(col, instance_or_collection, options = {}) # nodoc
def unload_relation(col)
end

def evaluate_default_value(column, value)
default = self.class.default(column)

case default
when NilClass
{column => value}
when Proc
{column => default.call}
when Symbol
{column => self.send(column)}
else
{column => default}
end
end

# issue #113. added ability to specify a proc or block
# for the default value. This allows for arrays to be
# created as unique. E.g.:
#
# class Foo
# include MotionModel::Model
# include MotionModel::ArrayModelAdapter
# columns subject: { type: :array, default: ->{ [] } }
# end
#
# ...
#
# This is not constrained to initializing arrays. You can
# initialize pretty much anything using a proc or block.
# If you are specifying a block, make sure to use begin/end
# instead of do/end because it makes Ruby happy.

def initialize_data_columns(column, value) #nodoc
self.attributes = {column => value || self.class.default(column)}
self.attributes = evaluate_default_value(column, value)
end

def column_as(col) #nodoc
Expand Down
2 changes: 1 addition & 1 deletion motion/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
# or forward port their code to take advantage
# of adapters.
module MotionModel
VERSION = "0.5.2"
VERSION = "0.5.3"
end
78 changes: 78 additions & 0 deletions spec/proc_defaults_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
describe "proc for defaults" do
describe "accepts a proc or block for default" do
describe "accepts proc" do
class AcceptsProc
include MotionModel::Model
include MotionModel::ArrayModelAdapter
columns subject: { type: :array, default: ->{ [] } }
end

before do
@test1 = AcceptsProc.create
@test2 = AcceptsProc.create
end

it "initializes array type using proc call" do
@test1.subject.should.be == @test2.subject
end
end

describe "accepts block" do
class AcceptsBlock
include MotionModel::Model
include MotionModel::ArrayModelAdapter
columns subject: {
type: :array, default: begin
[]
end
}
end

before do
@test1 = AcceptsBlock.create
@test2 = AcceptsBlock.create
end

it "initializes array type using begin/end block call" do
@test1.subject.should.be == @test2.subject
end
end

describe "accepts symbol" do
class AcceptsSym
include MotionModel::Model
include MotionModel::ArrayModelAdapter
columns subject: { type: :integer, default: :randomize }

def self.randomize
rand 1_000_000
end
end

before do
@test1 = AcceptsSym.create
@test2 = AcceptsSym.create
end

it "initializes column by calling a method" do
@test1.subject.should.be == @test2.subject
end
end

describe "scalar defaults still work" do
class AcceptsScalars
include MotionModel::Model
include MotionModel::ArrayModelAdapter
columns subject: { type: :integer, default: 42 }
end

before do
@test1 = AcceptsScalars.create
end

it "initializes column as normal" do
@test1.subject.should == 42
end
end
end
end

0 comments on commit 9c9ee6a

Please sign in to comment.