I use datamapper for the database. I have a table.
class ZedTable
include DataMapper::Resource
property :id, Serial
property :label, String
property :now, Boolean, :default => false
before :save do
ZedTable.all.update(:now => false)
self.now = true
end
end
That is, I want only one value was true. But when I save the data I get an error.
Failure/Error: Unable to find matching line from backtrace
SystemStackError:
stack level too deep
Why? And how do I solve this problem?
Thanks.
You get are getting stack too deep because when you call update, it first calls before :save hook again. The method you need is update!, it bypasses the hooks.
Related
I'm creating a Sinatra App using Datamapper.
With the following route, I'm attempting to print the record for an id. So localhost:9292/api/1 should return results for id=1
inside
get '/api/:id' do
I tried a couple things with varied results:
thing = Thing.get(params[:id])
thing.to_json
end
outputs 'null', but:
id_param = params[:id]
id_param
end
prints 1 as expected, and:
hardcoded_thing = Thing.get(1)
hardcoded_thing.to_json
end
correctly prints the hardcoded db record with id=1. So I must be losing it..
Any ideas?
Thanks!
For reference, here's my model:
class Thing
include DataMapper::Resource
include BCrypt
property :id, Serial, :key => true
property :created_at, DateTime
property :updated_at, DateTime
property :name, String, :length => 50
property :cafe_topic, Text
end
Try this:
get '/api/:id' do |id|
thing = Thing.get(id)
thing.to_json
end
I am having trouble with datamapper not updating a model. I can create and save models without issue. I have enabled raise_on_save_failure and checked the return value of update but see no errors.
Here is the model:
class UserProfile
include DataMapper::Resource
attr_accessor :id, :wants_hints, :is_beta_user
property :id, Serial #auto-increment integer key
property :is_beta_user, Boolean
property :wants_hints, Boolean
has 1, :user, :through => Resource
end
And here is where it is updated in the controller:
if user = User.get(request.session[:user])
if request.params[:user_profile]
beta = request.params[:user_profile].has_key?('is_beta_user')
hints = request.params[:user_profile].has_key?('wants_hints')
user.user_profile.update({:is_beta_user => beta, :wants_hints => hints}) # returns true
Log.puts user.user_profile.errors.each {|e| Log.puts e.to_s} # returns empty list []
end
end
When the controller is called update always returns true, and there are never errors in the error list. The datamapper log, which is set to :debug, only shows the SELECT queries for retrieving the user and user_profile and that is all. Why would I be able to save a newly created model, but not be allowed to update that same model?
Removing attr_accessor fixed the problem. From my research attr_accessor is used for attributes not in the database.
DataMapper's save and update do not necessarily produce an UPDATE sentence. It will only do so if the data held by the model object has changed. So, for example, in the following code the update will return true but will not produce an UPDATE:
# This generates an INSERT
user = User.create(:login => 'kintaro', :email => 'kintaro#example.com')
# This does NOT generate an UPDATE
user.update(:login => 'kintaro')
If you do this, however, an UPDATE will be produced:
# This generates an UPDATE
user.update(:login => 'kintaro22')
Maybe this is what's happening?
I have the following model in datamapper:
class Student
include DataMapper::Resource
property :id, Serial
# <snip>
property :permissions, String, :accessor => :protected, :required => true, :default => 'standard'
property :valid, Boolean, :default => false, :required => true
# <snip>
end
After requiring 'dm-validations' (version 1.1.0), and starting my Sinatra app, I recieve the following message:
/Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:81:in `valid?': wrong number of arguments (1 for 0) (ArgumentError)
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:81:in `save_self'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:1007:in `_save'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:1223:in `run_once'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:1006:in `_save'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:406:in `save'
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:69:in `save'
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations/support/context.rb:30:in `validation_context'
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:69:in `save'
<snip>
Is the 'valid' name I'm using for my model a reserved word? If it is, where can I find these words. I'm to the point of going on to trying to name it something like: 'student_valid' but now i'm just really curious about this.
Thanks
#valid? is a method that dm-validations adds. You cannot use "valid" as a property name because it automatically defines "valid?" method for a boolean property type which overrides dm-validations' valid?. Hence the error.
That's a tricky situation, I guess we need to improve the way we validate property names. Thanks for reporting this.
Well the way datamapper works, is that it uses method_missing at the end of the method call chain and finds your property. If there is a method with this same name then that is called rather than your property. Datamapper mixes in Validatable which has the method valid? Most of the time you learn what is reserved (Like all Object methods etc.) But if you want a full list you can do:
`myinstance.methods`
Anything that appears there will get called first.
I`m trying to figure out how to get serial and key attributes set for Resource object. Basic method DataMapper::Resource.attributes returns a collection of properties, but it does not say anything about types. Of course i can check it via system call: obj.class, but cant understand how to get type information from resource instance.
Example:
class Foo
include DataMapper::Resource
property :id, Serial
property :title, String, :required => true
property :created_at, Time, :required => true
property :flagged, Boolean, :default => false
end
So, is there any way to get this information about internal types for resource?
Not long time ago, i figured it out by myself. All model fields are basically instances of DataMapper::Property
So, all you need is to call ModelName.properties and get options like :index, :key, etc.
Description: http://yardoc.org/docs/datamapper-dm-core/DataMapper/Property
By any chance is it possible to create a conditional association with DataMapper?
For example:
I want the User have n Apps just if that user have the attribute :developer => true
something like this:
class User
include DataMapper::Resource
property :id, Serial
property :name, String, :nullable => false
property :screen_name, String, :nullable => false, :unique => true
property :email, String, :nullable => false, :unique => true, :format => :email_address
property :password, BCryptHash, :nullable => false
property :developer, Boolean, :default => false
#The user just gets apps if developer
has n :apps #,:conditions => "developer = 't'"
end
class App
include DataMapper::Resource
property :id, Serial
property :name, String, :nullable => false
belongs_to :user
end
I know that this would be possible by creating a subclass from User as a Developer::User and in that class, use the has n, but I really would like to know if its possible to make it directly on the association declaration.
Another way I also managed to do when using ARn was to extend the association and rewriting the methods for each action.
So on the extension module I could have something like this:
module PreventDeveloperActions
def new
if proxy_owner.developer?
super
else
raise NoMethodError, "Only Developers can create new applications"
end
end
# and so on for all the actions ...
end
But again, I really would like to avoid the use of this solutions if possible, but just if it's possible to perform a quick and direct method easily with DataMapper :)
Thanks in advance
At the moment, conditions that you include in the relationship declaration only apply to the target. So if the target model has an :active property, you can say things like has n, :apps, :active => true. Unfortunately you can't define relationships that are only active given the current state of the source (yet).
There are some proposals I'm considering to expand the Query logic in DM, but I'm unsure what the impact will be to the code, and what extra capabilities it will provide aside from this. It may be something we tackle after DM 1.0, since it also affects 50+ adapters and plugins.
STI is normally what I'd recommend for something like this, since it will allow you to define relationships that only exist for that type of object. Another approach would be to define the relationships as normal, mark the accessor/mutator methods as private, and then add a proxy method that does the equivalent of return apps if developer?.