Datamapper not saving ID, LocalJumpError - no block given - ruby

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!

Related

ActiveRecord::MultiparameterAssignmentErrors

I am new to rails 4 . I am using time_select to display time in my views.
While creating new event object I am getting error like:
ActiveRecord::MultiparameterAssignmentErrors (1 error(s) on assignment of
multiparameter attributes [error on assignment [12, 15] to time_begin
(Missing Parameter - time_begin(1))]):
In views:
<%= f.time_select :time_begin, :ampm => true, include_blank: true, :disabled => true %>
In my controller:
I have tried like this way:
params = event_params
hours = params['time_begin(4i)']
minutes = params['time_begin(5i)']
date = Date.strptime(params[:event_start], "%Y-%m-%d")
date = DateTime.civil(date.year,date.month, date.day, hours.to_i, minutes.to_i, 0, 0)
params[:time_begin] = date
#event = Event.new(params)
private
def event_params
params.require(:event).permit(:time_begin)
end
You need to modify your database field to be of type time not date_time. The following migration should do:
def up
change_column :events, :time_begin, :time
end
def down
change_column :events, :time_begin, :datetime
end
Note, that both types are stored same way in the database, the difference is that rails will purge (or rather reassign) its date part when you specify column to be a time. This unfortunately means that you will be able to call start_time.day, but the returned value will have no true meaning (always one), which might be a little misleading.

Datamapper TEXT limited to 65k characters by default — how to raise the limit?

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

Rails 3 - Validation for Time existence

I have a model with a Time attribute. I want to check that time can not be empty (better choice probably would be to check that input is time but i have no ideas how to deal with that). I tried this validation:
# id :integer not null, primary key
# school_class_id :integer
# meeting_time :time
class Meeting < ActiveRecord::Base
validates :meeting_time,
:presence => { :message => "can't be empty!" }
end
Then i tried to check this in spec and this fails (empty time is ok but it should not be). What do i do wrong?
Rspec:
# id :integer not null, primary key
# school_class_id :integer
# meeting_time :time
require 'spec_helper'
describe Meeting do
before(:each) do
#class = FactoryGirl.create( :school_class )
#attr_meeting = {
:meeting_theme => 'Text is here',
:meeting_date => "#{Date.today}",
:meeting_time => "#{Time.now}",
:meeting_room => '1234'
}
end
describe "Validations" do
describe "Rejection" do
it "should reject blank time" do
wrong_attr = #attr_meeting.merge( :meeting_time => " " )
#class.meetings.build( wrong_attr ).should_not be_valid
end
end
end
end
Error:
Meeting Validations Rejection should reject blank time
Failure/Error: #class.meetings.build( wrong_attr ).should_not be_valid
expected valid? to return false, got true
In your example, you assign " " to the meeting_time, not nil or "". But Time object somehow can be successfuly generated from non-empty, even blank string. Setting meeting_time to "" or nil should solve yout problem.
Maybe I don't fully understand something, but I think it's not very logical and predictable behaviour. Need to take a look into the sources.

Sinatra and Datamapper not able to save an array to the datatype Object

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.

Datamapper validations, empty errors

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

Resources