I have a simple model with the following properties:
property :title, String,
:required => true,
:length => 1..200
property :body, String,
:required => true,
:length => 1..200
When I save it (using the save method) with an title that doesn't validate, it won't save, but I get the follow object:
<DataMapper::Validations::ValidationErrors:0x00000003133e10
#resource=#<Pun #id=nil #image="" #title="" #body="dsfsdf" #description=nil
#published=nil #user_id=1 #round_id=nil>, #errors={}>
So the errors hash is empty. However, when it's the body property that's empty, the errors hash detect its problem.
Any idea on why the errors hash is empty when it involves the :title property?
Thanks
So knowtheory wrote in a comment on a blog (in 2010)
that some helper methods were created to compensate for this. His examples for a User model:
User.raise_on_save_failure = true - for all user instances to blow up.
DataMapper.raise_on_save_failure = true
- for EVERYTHING to blow up if it doesn’t save successfully.
Source definitions: dm-core/model.rb and dm-core/resource.rb
Related
Is it possible to update a property in DataMapper if :key is set to true?
Say, for example, I have a model set up like this:
class Post
include DataMapper::Resource
property :slug, Text, :unique => true, :key => true
# ...
end
and I made a new instance of this with :slug => "example-post-title".
I tried to update it by accessing the stored
#post = Post.get("example-post-title")
#=> #<Post #slug="example-post-title" ...>
#post.slug = "example-post-title-2"
#=> "example-post-title-2"
#post.save
#=> true
#post = Post.get("example-post-title-2")
#=> nil
but as you can see the slug was never updated. I also tried using the Post#update method:
#post = Post.get("example-post-title")
#=> #<Post #slug="example-post-title" ...>
#post.update(:slug => "example-post-title-2")
#=> true
#post = Post.get("example-post-title-2")
#=> nil
Looking in the database, the index column is not changed by either of these examples. It remains as example-post-title rather than example-post-title-2.
According to the docs, the Post#update method, similar to the Post#save method, should return true if the operation was successful, and false if it was not. It is returning true here, but it's not actually updating the record.
I've searched and searched and can't find anything about it on the Internet. Neither StackOverflow nor the DataMapper rdoc had anything about updating the key.
I know that I can have a unique Serial property and just get instances of Post using the slug (as in, make the Serial property the key instead of the slug), but I'm looking for a way to do it without that, if at all possible.
My hunch is that you can't update a key. According to the doc, they are protected against mass assignment:
Natural Keys are protected against mass-assignment, so their setter= will need to be called individually if you're looking to set them.
They don't talk about updating them but usually in "key => value" stores it is impossible or deprecated to update the key. I'd assume that's the case here as well, even though I can't find any hard evidence to give to you : /
I recently refactored my DataMapper code, slowly rolling it out, and got it working on one database, but now I'm encountering problems when rolling it out to my expense database. Couldn't find the answer anywhere, and I've tried lots of fiddling.
I have a form (using Sinatra) that takes several inputs, prepended with "expense_", and it should take that data, send it to the database, and upload a receipt image to S3. But I'm getting an id of nil, and a LocalJumpError if I turn on DataMapper error reporting.
Here's my code:
DB update method:
def dm_update(method_list,model,params,param_prefix,use_last_entry_if_param_empty=true)
model_data = model.new
method_list.each do |meth|
# e.g. param is :expense_date, db column is :date
param_name = (param_prefix + meth.to_s).to_sym
param = params[param_name]
if use_last_entry_if_param_empty
# If true, use most recent entry from db - for use in settings changing
data = param.empty? ? model.last[meth] : param
else
data = param
end
model_data.send("#{meth}=", data)
end
model_data.save
end
Taking params and sending to method:
file_name = ("#{params[:expense_date]}_#{params[:expense_time].gsub(/:/, '')}_#{params[:expense_receipt_file][:filename]}")
temp_file = params[:expense_receipt_file][:tempfile]
expense_column_list = [:date,:time,:price,:currency,:description,:source,:receipt_filename]
params[:expense_receipt_filename] = file_name
dm_update(expense_column_list,Expense,params,"expense_",false)
upload(file_name, temp_file, "foo_bucket")
Datamapper class:
class Expense
include DataMapper::Resource
property :id, Serial, :required => true, :key => true
property :date, Date, :required => true
property :time, Time, :required => true, :default => Time.now
property :price, Float, :required => true
property :currency, String, :required => true, :default => "GBP"
property :description, String, :required => true
property :source, String, :required => true
property :receipt_filename, String
end
Ok, the answer to this was fairly simple when it came down to it.
I defactored the code, and went through it line by line.
The date param was stored in the format yyyy-mm-dd, and the time param was stored as hh:mm. Datamapper didn't like that time, which I'd fixed in the original version, but had failed to in the refactored version.
The fix ended up being to add the line:
temptime = Time.parse(params[:date] + " " + params[:time])
params[:time] = temptime
prior to calling the dm_update method. This fixed it, and it now works!
I try to config simple_form to always set all fields NOT required by default.
But I still need this when I put :required => true in the view.
Then I go to config/initializers/simple_form.rb and set it like this.
config.wrappers :default, :class => :input, :required => false,
:hint_class => :field_with_hint, :error_class => :field_with_errors do |b|
and set config.required_by_default = false
But asterisk still show up.
Thanks for any suggestion.
Rafaiel,
I had the same problem in Rails 4.0, not sure what you're using.
The best solution I've found is to go to config/locales/simple_form.en.yml and change the first lines like this (the mark: line is the one you change):
en:
simple_form:
"yes": 'Yes'
"no": 'No'
required:
text: 'required'
mark: '' #mark was previously '*', which puts an asterisk in the display!
It's also possible to change SimpleForm setup.
SimpleForm.setup do |config|
# Whether attributes are required by default (or not). Default is true.
config.required_by_default = false
end
Take this into account: (taken from simple form github page)
Required fields are marked with an * prepended to their labels.
By default all inputs are required. When the form object includes
ActiveModel::Validations (which, for example, happens with Active
Record models), fields are required only when there is presence
validation. Otherwise, Simple Form will mark fields as optional. For
performance reasons, this detection is skipped on validations that
make use of conditional options, such as :if and :unless.
I have a class that needs to have TEXT up to about 300k characters and it's stored in a PostgreSQL db.
Postgres itself has no problem with megabyte blobs, (eventually I will store them in S3), but Datamapper has a default limit of '65k characters' for a TEXT:
By default, DataMapper supports the following primitive types:
TrueClass, Boolean
String
Text (limit of 65k characters by default)
I want to do something like
property :id, Serial
property :name, String, :index => true
property :posted, DateTime
property :info, DataMapper::Types::Text, :lazy => false
property :data, DataMapper::Types::Text, :limit => 500000 # needs to be big is :limit correct?5
I know the lazy part is OK, because I got it from http://datamapper.rubyforge.org/dm-core/DataMapper/Property.html — but what is the keyword to use to override the limit on a TEXT field:
:length?
:maximum?
:limit?
Or something else?
OK,
Turns out :length does work.
class SomeClass
include DataMapper::Resource
property :id, Serial
property :name, String, :index => true # strings are actively loaded
property :posted, DateTime
property :info, Text, :lazy => false # This is short stuff a hundred or so bytes long that I want loaded
property :data, Text, :length => 500000 # Text is lazy loaded by default, but I also need to override the default length limit which is 65536 chars in DM
I have an HTML form which uses the following Sinatra code to handle POST for the url '/add-artist':
post '/add-artist' do
if logged_in?
a = Artist.new
a.name = params[:name]
a.website = params[:website]
a.facebook = params[:facebook]
a.created_by = session[:user_id]
a.created_at = Time.now
a.updated_by = session[:user_id]
a_updated_at = Time.now
a.views = 0
a.save
#user = User.get session[:user_id]
#user.artists.push(a.id)
#user.save
redirect '/'
end
end
The object 'a' is being saved but '#user' is not. I guess more specifically, the value '#user.artists' is not being updated. If you need more info, please ask but I have a feeling that you Ruby vets will find the problem in the code I provided.
UPDATE
Here's some additional info. I was able to reproduce the error in irb. First here's my class definition for 'User'.
# dm_models.rb
require 'data_mapper'
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/event_review.db")
class User
include DataMapper::Resource
property :id, Serial
property :email, String
property :password, String
property :user_name, String
property :birthdate, Date
property :city, String
property :state, String
property :zip, String
property :geodata, Object
property :bio, Text
property :friends, Object
property :events, Object
property :event_reviews, Integer
property :artists, Object
property :artist_reviews, Integer
property :venues, Object
property :venue_reviews, Integer
property :created_at, DateTime
property :updated_at, DateTime
property :views, Integer
has n, :reviews
end
Here is the irb
>> require 'sinatra'
=> true
>> require 'data_mapper'
=> true
>> require './models/dm_models.rb'
=> true
>> require 'geokit'
=> true
>>
?> include Geokit::Geocoders
=> Object
>> u = User.get 8
=> #<User #id=8 #email="km#km.com" #password="km" #user_name="Katherine Miller" #birthdate=#<Date: 4895485/2,0,2299161> #city="Burbank" #state="CA" #zip="91501" #geodata=#<Geokit::GeoLoc:0x10150d4d8 #street_number=nil, #suggested_bounds=#<Geokit::Bounds:0x10150cf88 #sw=#<Geokit::LatLng:0x10150cd80 #lng=-118.315043, #lat=34.1766949>, #ne=#<Geokit::LatLng:0x10150cee8 #lng=-118.27996, #lat=34.221666>>, #lng=-118.2935891, #zip="91501", #state="CA", #precision="zip", #province=nil, #all=[#<Geokit::GeoLoc:0x10150d4d8 ...>], #street_address=nil, #provider="google", #city="Burbank", #lat=34.2039087, #country_code="US", #full_address="Burbank, CA 91501, USA", #street_name=nil, #accuracy=5, #country="USA", #success=true> #bio=<not loaded> #friends=[] #events=["13", "14", "15", "16", "28", "29"] #event_reviews=7 #artists=[] #artist_reviews=1 #venues=[] #venue_reviews=0 #created_at=#<DateTime: 70729968253/28800,-5/24,2299161> #updated_at=#<DateTime: 1178838019/480,-5/24,2299161> #views=56>
>>
?> u.artists
=> []
>> u.artists.push "5"
=> ["5"]
>> u.save
=> true
>> u = User.get 8
=> #<User #id=8 #email="km#km.com" #password="km" #user_name="Katherine Miller" #birthdate=#<Date: 4895485/2,0,2299161> #city="Burbank" #state="CA" #zip="91501" #geodata=#<Geokit::GeoLoc:0x1014e8638 #street_number=nil, #suggested_bounds=#<Geokit::Bounds:0x1014e80e8 #sw=#<Geokit::LatLng:0x1014e7eb8 #lng=-118.315043, #lat=34.1766949>, #ne=#<Geokit::LatLng:0x1014e8048 #lng=-118.27996, #lat=34.221666>>, #lng=-118.2935891, #zip="91501", #state="CA", #precision="zip", #province=nil, #all=[#<Geokit::GeoLoc:0x1014e8638 ...>], #street_address=nil, #provider="google", #city="Burbank", #lat=34.2039087, #country_code="US", #full_address="Burbank, CA 91501, USA", #street_name=nil, #accuracy=5, #country="USA", #success=true> #bio=<not loaded> #friends=[] #events=["13", "14", "15", "16", "28", "29"] #event_reviews=7 #artists=[] #artist_reviews=1 #venues=[] #venue_reviews=0 #created_at=#<DateTime: 70729968253/28800,-5/24,2299161> #updated_at=#<DateTime: 1178838019/480,-5/24,2299161> #views=56>
>> u.artists
=> []
>> u.artists.class
=> Array
The description of the above code: I retrieve user with id==8, push the value "5" into it. This appears to be successful. I save user#8. Then I re-retrieve user#8 and look at the artists value and it is an empty array.
And finally, I am able to update other fields like "artist_reviews". Is this because I am defining the datatype to be 'Object' for artists, events and venues? This problem exists for all of those fields.
Thanks for the help.
What do the logs say? Can you push to #user.artists? Is it an Array? It might fail validation and you cannot complete save.
I asked this a while ago but I'm pretty certain the solution is to serialize the object. In this case it was an array of integers. I'm not sure if I'll have the time to update this with a detailed solution but an array cannot be stored directly in a relational database. The array object must be 'serialized', essentially converted to a string. In this example the datatype for the artist attribute would then be text.
You could manually convert the artist column to an array (from string) push the new integer(s) into the array and then convert back to string and save. I'm assuming there is an automated way to do this but that's the idea.
Furthermore, this entire example is a poor way to handle associations. The better way to do this is to have a one-to-many association where there is an ArtistUser table which has two columns artist_id and user_id. Every new association is represented as a new row in the ArtistUser table.