data_mapper: Can't save Time object - ruby

I am attempting to persist an object that has a Time-type field. On create, I attempt to set the sleep_value to a valid Time object but the save seems to fail silently (even though raise_on_save_failure is set to true).
If I try to search for the object via Sleep.all or Sleep.get(1), i am getting an error argument out of range.
The only value I can pass it to make it persist is Time.now(). If I do a Sleep.all, that object with sleep_time => Time.now is returned.
gems:
data_mapper (1.2.0)
sqlite3 (1.3.10)
require 'sqlite3'
require 'data_mapper'
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/test.db")
DataMapper::Model.raise_on_save_failure = true
class Sleep
include DataMapper::Resource
property :id, Serial
property :sleep_time, Time
end
DataMapper.finalize.auto_migrate!
Sleep.create(:sleep_time => Time.new(2015, 10, 1))
Sleep.all
UPDATE: I have done a bit more trial and error and have found the following values are compatible with the time field (changed the name from sleep_time to st)
Sleep.create(:st => (Time.now - 1))
Sleep.create(:st => Time.at((Time.now - Time.at(1000))))
And the following values do not save (no error is thrown but records are nt persisted)
Sleep.create(:st => Time.new(2015, 10, 1, 2, 2, 2, "+02:00"))
Sleep.create(:st => Time.new(2002))
Sleep.create(:st => Time.new((Time.now - Time.new(2002)))) #out of range
Sleep.create(:st => Time.at(2)) #out of range

I used the fact that I could use with Time.now() and made this helper method. I call it to save my Time properties.
def self.convert_to_dm_friendly_time(target_time)
today = Time.now
return today + target_time.to_i - today.to_i #basically starting the object from today and then taking today out.
end
Damn, this was an ugly solution. Surely there is an easier way?

Related

Activerecord store only returns empty hash after rails 5 upgrade

I am in the process of upgrading a rails 4.2 application to rails 5. One of the models uses an ActiveRecord::Store. When retrieving records after the upgrade, said store only returns an empty Hash. I have no idea why that's happening and wasn't able to find anything in the changelog. So any help and explanation would be appreciated.
Here's the output of the rails 4 console:
=> #<StoredDataQuery ..., config: {"start_date"=>6, "end_date"=>0, ...>
and rails 5:
=> #<StoredDataQuery ..., config: {}, ...
psql output:
development=# SELECT config FROM stored_data_queries WHERE id=1;
config
---------------------------------------------
--- !ruby/hash:ActionController::Parameters+
start_date: 6 +
end_date: 0 +
interval: day +
(1 row)
Looking at the SQL output, I'm suspecting it has something to do with the data being serialized as ActionController::Parameters.
Thanks for your help!
Here's how to fix it in sql (postgres):
UPDATE stored_data_queries SET config = replace(config, 'ActionController::Parameters', 'ActiveSupport::HashWithIndifferentAccess');
Same here, after Upgrading Zammad from Rails 4.2 to Rails 5.0. After some research I found out that active record is only reading ActiveSupport::HashWithIndifferentAccess from store anymore (other classes are skipped).
So for migration you can overwrite ActiveRecord::Store::IndifferentCoder.as_indifferent_hash in a migration, read all related records from database and just save them back (then all ActionController::Parameters are converted to ActiveSupport::HashWithIndifferentAccess).
For me the following migration (under Rails 5.0) has worked to convert all ActionController::Parameters to ActiveSupport::HashWithIndifferentAccess:
require 'active_record/store'
module ActiveRecord
module Store
class IndifferentCoder
def self.as_indifferent_hash(obj)
case obj
# re-enable using ActionController::Parameters in stores,
# convert them to ActiveSupport::HashWithIndifferentAccess
# at reading from db
when ActionController::Parameters
obj.permit!.to_h
# /re-enable
when ActiveSupport::HashWithIndifferentAccess
obj
when Hash
obj.with_indifferent_access
else
ActiveSupport::HashWithIndifferentAccess.new
end
end
end
end
end
class FixedStoreUpgrade45 < ActiveRecord::Migration[5.0]
def up
[Macro, Taskbar, Calendar, Trigger, Channel, Job, PostmasterFilter, Report::Profile, Setting, Sla, Template].each do |class_name|
class_name.all.each do |record|
begin
record.save!
rescue => e
Rails.logger.error "Unable to save/update #{class_name}.find(#{record.id}): #{e.message}"
end
end
end
end
end

Datamapper not saving ID, LocalJumpError - no block given

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!

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.

ruby Datamapper is not inserting all records

I have this ruby function which yields 1095 records MusicTab::FOps.gen_list('/fun/Music')
and I want to store them using datamapper. When I do this
MusicTab::FOps.gen_list('/fun/Music') do |arr_f|
#files=Files.create(
:file_path => arr_f[0],
:title => arr_f[1],
:album => arr_f[2],
:artist => arr_f[3] )
end
only 154 records are inserted, I don't understand what is so special about these records.
If I do this I get nil for p #files.id all the other records other than those 154 records which gets stored.
MusicTab::FOps.gen_list('/fun/Music') do |arr_f|
#files=Files.create(
:file_path => arr_f[0],
:title => arr_f[1],
:album => arr_f[2],
:artist => arr_f[3] )
p #files.id
p #files.title
p #files.album
end
If I just print the values I can see all the values like
counter=0
MusicTab::FOps.gen_list('/fun/Music') do |arr_f|
p arr_f
counter=counter+1
end
counter
please help..
Regards
try looking at #files.errors. My guess is that you are failing a validation and it isn't getting saved.
You can try enabling some Debug logging and see if that helps to isolate the problem.
DataMapper::Logger.new($stdout, :debug)
It would be helpful to see your Model Definition for the Files object, and a sample of the data that doesn't get saved, and one that does. The last time I ran into this issue was when I needed to set the size on my text fields as I was overflowing the default.

Chaining datamapper relationships across different repositories

class A
include DataMapper::Resource
def self.default_repository_name
:alt_db
end
property :aid, Integer, :key => true
# other stuff
belongs_to :b, :model => 'B', :child_key => [ :bid ]
end
class B
include DataMapper::Resource
# this one is in the default repo
property :bid, Integer, :key => true
# other stuff
belongs_to :c, :model => 'C', :child_key => [ :cid ]
end
class C
include DataMapper::Resource
# this one is in the default repo
property :cid, Integer, :key => true
# other stuff
end
If I just have A and B, this works fine. If I add C, however, I get an error:
dm-core/model/property.rb:73:in `new': wrong number of arguments (4 for 3) (ArgumentError)
If I want to make a chain of relationships with DataMapper, so that I can give an ID in one place and get a piece of data that's, say, four tables away through a series of references to subsequent tables' primary key ID field, how can I do this?
EDIT: Digging into the DM source from the stack trace:
DataMapper.repository(other_repository_name) do
properties << klass.new(self, name, options, type)
end
That's where the error is raised. Indeed, in this case klass is a DataMapper Integer property, and it's initialize method only accepts three options (model, name, and an options hash).
This whole block is only executed because I'm using more than one repository, though B and C are in the same one so I don't know if that sheds any light on why it's erroring on the cid property.
EDIT2:
I have tried all permutations, and it appears that when you're chaining, once you cross a database-boundary, that must be the end of the chain. For example, since A is :alt_db and B is :default, B is as deep as I can go, regardless of whether C is :default, :alt_db, or a third option.
If instead both A and B were :default, or both were :alt_db, and then C were the opposite one, C would be as deep as I could go.
I don't understand this behavior really, though.
You found a bug actually. It's been fixed in master. You can try grabbing sources from git and see if it works.
Your code works fine for me.
irb(main):001:0> A.first.b.c
DEBUG - "(0.001168) SELECT "aid", "bid" FROM "as" ORDER BY "aid" LIMIT 1"
DEBUG - "(0.000337) SELECT "bid", "cid" FROM "bs" WHERE "bid" = 2 LIMIT 1"
DEBUG - "(0.000046) SELECT "cid" FROM "cs" WHERE "cid" = 3 LIMIT 1"
=> #<C #cid=3>
My gem is dm-core-1.1.0, you should check your version.
It turns out this was a small issue with DataMapper chaining across repositories. Submitted to them and it's allegedly been fixed already!
http://datamapper.lighthouseapp.com/projects/20609/tickets/1506-can-only-chain-up-to-first-time-changing-default-repository#ticket-1506-1

Resources