When should a resource be immutable?

148 views
Skip to first unread message

Zhi-Qiang Lei

unread,
Jan 10, 2011, 10:23:52 AM1/10/11
to datam...@googlegroups.com
Dear All,

Could anyone answer me when a resource should be immutable? And why? Thanks.

Best regards,
Zhi-Qiang Lei
zhiqia...@gmail.com

Piotr Solnica

unread,
Jan 10, 2011, 10:32:45 AM1/10/11
to DataMapper
Hey,

A resource becomes immutable after you destroy it. This prevents from
any further attempts on saving the resource or modifying its
attributes.

Cheers

# solnic
> zhiqiang....@gmail.com

Ted Han

unread,
Jan 10, 2011, 10:34:12 AM1/10/11
to datam...@googlegroups.com
Objects are immutable when they've been deleted or frozen for some reason.  Deleting a record from your data store will always result in that record being frozen.

Is there a specific problem that you're having?

-T


--
You received this message because you are subscribed to the Google Groups "DataMapper" group.
To post to this group, send email to datam...@googlegroups.com.
To unsubscribe from this group, send email to datamapper+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/datamapper?hl=en.


Jonathan Stott

unread,
Jan 10, 2011, 10:39:36 AM1/10/11
to datam...@googlegroups.com
Also, you will retreive an immutable resource if you define a Model
without a key.

To check for this (and to initialize relationships), you should always
run DataMapper.finalize after defining all your models.

Regards
Jon

Martin Gamsjaeger

unread,
Jan 10, 2011, 10:52:53 AM1/10/11
to datam...@googlegroups.com
To add to what Jonathan said, if you *retrieve* a resource without
it's key, you get back an immutable resource too.

So if you have a Person model with a Serial id and you do something like:

Person.all(:job => 'coder', :fields => [ :name ])

you will get back a Collection of immutable resources, because you've
told it specifically to not include the key property (:id)

cheers
snusnu

Martin Gamsjaeger

unread,
Jan 10, 2011, 10:58:07 AM1/10/11
to datam...@googlegroups.com
I forgot to give an explanation of why DM behaves like that. It's
probably trivial, but why not add it here for completeness.

If you have a resource in memory, but you don't know it's key, there's
no way to persist it back to the datastore.

Zhi-Qiang Lei

unread,
Jan 10, 2011, 11:42:11 AM1/10/11
to datam...@googlegroups.com
Hi,

I have some code as follow.

class Loan
  include DataMapper::Resource
  belongs_to :loaner, Person, :key => true
  belongs_to :loanee, Person, :key => true
  property :currency, Enum[*CURRENCY_CODES], :key => true
  property :amount, Decimal, :scale => 2, :default => 0

  def self.count!(money_flow)
    loan = get(money_flow.currency, money_flow.giver_id, money_flow.receiver_id)
    loan.amount += money_flow.amount
  end

  # more code...
end

class MoneyFlow
  include DataMapper::Resource
  belongs_to :giver, Person
  belongs_to :receiver, Person

  property :id, Serial
  property :amount, Decimal, :scale => 2, :min => 0.01
  property :currency, Enum[*CURRENCY_CODES], :default => :CNY

  # more code...
end

When I test the "count!" class method as follow:

  describe "#count!" do
    subject { lambda { Loan.count!(money_flow) } }
    context "when a loan exists from giver to receiver" do
      let!(:loan) { Loan.gen }
      let(:money_flow) { MoneyFlow.gen(:giver => loan.loaner, :receiver => loan.loanee, :currency => loan.currency) }
      it { should_not change(Loan, :count) }
    end
  end

It tells me I got a Immutable Error. I feel strange on that. They have keys, and I didn't destroy the record. Can you see why? Thanks.

  1) Loan#count! when a loan exists from giver to receiver 
     Failure/Error: subject { lambda { Loan.count!(money_flow) } }
     DataMapper::ImmutableError:
       Immutable resource cannot be modified
     # ./lib/models.rb:49:in `count!'
     # ./spec/models_spec.rb:59:in `block (4 levels) in <top (required)>'
     # ./spec/models_spec.rb:63:in `block (4 levels) in <top (required)>'

RipTheJacker

unread,
Jan 10, 2011, 6:05:54 PM1/10/11
to DataMapper
You should probably just change that spec. The element that you want
to test is a proc (you hope) getting invoked inside of the subject
block, which is a weird pattern, and you don't need the #let blocks
really, since you only use those objects once.

Try:

describe "#count!" do
specify "when a loan exists from giver to receiver" do
loan = Loan.gen
money_flow = MoneyFlow.gen(:giver => loan.loaner, :receiver =>
loan.loanee, :currency => loan.currency)
expect do
Loan.count!(money_flow)
end.to_not change(Loan, :count)
end
end


I'm assuming you are using rspec 2, though it will work in rspec 1.x
with a syntax change. If you still get that error, post the new
backtrace here. At the very least the spec and the backtrace will be
more meaningful this way, and may be easier to debug (I hope).
> > On Mon, Jan 10, 2011 at 10:23 AM, Zhi-Qiang Lei <zhiqiang....@gmail.com> wrote:
> > Dear All,
>
> > Could anyone answer me when a resource should be immutable? And why? Thanks.
>
> > Best regards,
> > Zhi-Qiang Lei
> > zhiqiang....@gmail.com
>
> > --
> > You received this message because you are subscribed to the Google Groups "DataMapper" group.
> > To post to this group, send email to datam...@googlegroups.com.
> > To unsubscribe from this group, send email to datamapper+...@googlegroups.com.
> > For more options, visit this group athttp://groups.google.com/group/datamapper?hl=en.
>
> > --
> > You received this message because you are subscribed to the Google Groups "DataMapper" group.
> > To post to this group, send email to datam...@googlegroups.com.
> > To unsubscribe from this group, send email to datamapper+...@googlegroups.com.
> > For more options, visit this group athttp://groups.google.com/group/datamapper?hl=en.
>
> Best regards,
> Zhi-Qiang Lei
> zhiqiang....@gmail.com

Zhi-Qiang Lei

unread,
Jan 11, 2011, 12:22:40 AM1/11/11
to datam...@googlegroups.com
Hi,

I still get the error with your spec code, here is the backtrace.

Failures:

1) Loan#count! when a loan exists from giver to receiver

Failure/Error: Loan.count!(money_flow)


DataMapper::ImmutableError:
Immutable resource cannot be modified

# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/dm-core-1.0.2/lib/dm-core/resource/state/immutable.rb:16:in `set'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/dm-core-1.0.2/lib/dm-core/model/property.rb:251:in `amount='


# ./lib/models.rb:49:in `count!'

# ./spec/models_spec.rb:64:in `block (4 levels) in <top (required)>'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/lib/rspec/matchers/change.rb:17:in `call'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/lib/rspec/matchers/change.rb:17:in `matches?'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/lib/rspec/expectations/handler.rb:34:in `handle_matcher'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/lib/rspec/expectations/extensions/kernel.rb:50:in `should_not'
# ./spec/models_spec.rb:63:in `block (3 levels) in <top (required)>'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:49:in `instance_eval'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:49:in `block (2 levels) in run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:98:in `with_around_hooks'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:46:in `block in run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:91:in `block in with_pending_capture'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:90:in `catch'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:90:in `with_pending_capture'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example.rb:45:in `run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example_group.rb:261:in `block in run_examples'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example_group.rb:257:in `map'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example_group.rb:257:in `run_examples'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example_group.rb:231:in `run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example_group.rb:232:in `block in run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example_group.rb:232:in `map'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/example_group.rb:232:in `run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/command_line.rb:27:in `block (2 levels) in run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/command_line.rb:27:in `map'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/command_line.rb:27:in `block in run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/reporter.rb:12:in `report'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/command_line.rb:24:in `run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/runner.rb:55:in `run_in_process'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/runner.rb:46:in `run'
# /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/core/runner.rb:10:in `block in autorun'

> For more options, visit this group at http://groups.google.com/group/datamapper?hl=en.
>


Best regards,
Zhi-Qiang Lei
zhiqia...@gmail.com

RipTheJacker

unread,
Jan 11, 2011, 2:15:47 PM1/11/11
to DataMapper
Ok. It's because of this:

property :currency, Enum[*CURRENCY_CODES], :key => true


You can't have :key => true on an Enum property since it is not
unique.


On Jan 10, 11:22 pm, Zhi-Qiang Lei <zhiqiang....@gmail.com> wrote:
> Hi,
>
> I still get the error with your spec code, here is the backtrace.
>
> Failures:
>
>   1) Loan#count! when a loan exists from giver to receiver
>      Failure/Error: Loan.count!(money_flow)
>      DataMapper::ImmutableError:
>        Immutable resource cannot be modified
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/dm-core-1.0.2/lib/dm-core/r esource/state/immutable.rb:16:in `set'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/dm-core-1.0.2/lib/dm-core/m odel/property.rb:251:in `amount='
>      # ./lib/models.rb:49:in `count!'
>      # ./spec/models_spec.rb:64:in `block (4 levels) in <top (required)>'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/li b/rspec/matchers/change.rb:17:in `call'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/li b/rspec/matchers/change.rb:17:in `matches?'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/li b/rspec/expectations/handler.rb:34:in `handle_matcher'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-expectations-2.4.0/li b/rspec/expectations/extensions/kernel.rb:50:in `should_not'
>      # ./spec/models_spec.rb:63:in `block (3 levels) in <top (required)>'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:49:in `instance_eval'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:49:in `block (2 levels) in run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:98:in `with_around_hooks'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:46:in `block in run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:91:in `block in with_pending_capture'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:90:in `catch'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:90:in `with_pending_capture'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example.rb:45:in `run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example_group.rb:261:in `block in run_examples'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example_group.rb:257:in `map'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example_group.rb:257:in `run_examples'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example_group.rb:231:in `run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example_group.rb:232:in `block in run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example_group.rb:232:in `map'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/example_group.rb:232:in `run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/command_line.rb:27:in `block (2 levels) in run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/command_line.rb:27:in `map'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/command_line.rb:27:in `block in run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/reporter.rb:12:in `report'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/command_line.rb:24:in `run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/runner.rb:55:in `run_in_process'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/runner.rb:46:in `run'
>      # /Users/siegfried/.rvm/gems/ruby-1.9.2-p136/gems/rspec-core-2.4.0/lib/rspec/ core/runner.rb:10:in `block in autorun'

Zhi-Qiang Lei

unread,
Jan 12, 2011, 1:28:49 AM1/12/11
to datam...@googlegroups.com
Hi,

This model also has two more keys, they make a composite keys. This will also make it immutable?

>>>> belongs_to :loaner, Person, :key => true
>>>> belongs_to :loanee, Person, :key => true

> For more options, visit this group at http://groups.google.com/group/datamapper?hl=en.
>


Best regards,
Zhi-Qiang Lei
zhiqia...@gmail.com

RipTheJacker

unread,
Jan 12, 2011, 2:17:27 PM1/12/11
to DataMapper
Those won't make it immutable, but the key does need to be unique. So,
unless you can only have ONE Loan per Person you should probably
change those too. Rather than making them keys you probably mean to
have them as indexes, which I DataMapper does for you, and add an :id
Serial primary key to the model, the same as it is in your MoneyFlow
model.

Zhi-Qiang Lei

unread,
Jan 13, 2011, 2:39:37 AM1/13/11
to datam...@googlegroups.com
OK, thanks. I will make it in that way.

> For more options, visit this group at http://groups.google.com/group/datamapper?hl=en.
>


Best regards,
Zhi-Qiang Lei
zhiqia...@gmail.com

Zhi-Qiang Lei

unread,
Jan 13, 2011, 2:53:48 AM1/13/11
to datam...@googlegroups.com
Excuse me, a little more discussion please. In my past design, there should be ONLY ONE Loan record between two people in a specific currency. The composite keys match this requirement, don't they? But to make it immutable.

On Jan 13, 2011, at 3:17 AM, RipTheJacker wrote:

> For more options, visit this group at http://groups.google.com/group/datamapper?hl=en.
>


Best regards,
Zhi-Qiang Lei
zhiqia...@gmail.com

RipTheJacker

unread,
Jan 13, 2011, 6:39:13 PM1/13/11
to DataMapper
Yes, the composite keys match your requirement, so you can leave them
for the belongs_to associations. And I'm starting to think this may
really be a bug. See if this works:

remove the :key => true from :currency and add this:
validates_uniqueness_of :currency, :scope => [:loaner_id, :loanee_id]
> ...
>
> read more »

Zhi-Qiang Lei

unread,
Jan 14, 2011, 1:08:23 AM1/14/11
to datam...@googlegroups.com
Removing the key on currency only will lead to only one Loan record between two people, and they cannot have other Loan in different currency. So I remove all of them (keys), and add a id Serial property. With this validation, not ImmutableError recurs yet.
Do you think I should submit a ticket for this?

> For more options, visit this group at http://groups.google.com/group/datamapper?hl=en.
>


Best regards,
Zhi-Qiang Lei
zhiqia...@gmail.com

RipTheJacker

unread,
Jan 14, 2011, 4:07:44 PM1/14/11
to DataMapper
A ticket would be great, and please include a link to this discussion
in there as well.
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages