Rails 3 - Validation for Time existence - ruby

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.

Related

Why can't my script access classes that are in different directory?

I am trying to access classes DealState and NotAnEndState that are in another directory, where I have a lib called move-to-go.
move-to-go folder contains modules where the one in my example is named deal_state.rb. When i open deal_state.rb it contains the following the code below.
Path to lib: F:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\move-to-go-5.3.0\lib\move-to-go
module MoveToGo
module DealState
# This is the default, a deal with a status with this state is
# currently being worked on.
NotAnEndState = 0
# The deal has reached a positive end state, eg we have won
# the deal.
PositiveEndState = 1
# The deal has reached a negative end state, eg we have lost
# the deal.
NegativeEndState = -1
end
end
Path to my code: C:Users/Shahin/MigrationFolder/converter.rb
class Converter
def configure(rootmodel)
rootmodel.settings.with_organization do |organization|
organization.set_custom_field( { :integration_id => 'source', :title => 'Källa', :type => :Link } )
end
rootmodel.settings.with_person do |person|
person.set_custom_field( { :integration_id => 'source', :title => 'Källa', :type => :String} )
end
rootmodel.settings.with_deal do |deal|
assessment is default DealState::NotAnEndState
deal.add_status( {:label => '1. Kvalificering' })
deal.add_status( {:label => '2. Deal closed', :assessment => MoveToGo::DealState::PositiveEndState })
deal.add_status( {:label => '4. Deal lost', :assessment => MoveToGo::DealState::NegativeEndState })
end
end
When I execute my script I get this error message:
C:Users/MyUserName/MigrationFolder/converter.rb:63:in `block in configure': uninitialized constant Converter::DealState (NameError)
Did you mean? DEAL_SHEET
New things have however come to light. The error message seems to have an issue with the Converter-class, but i cant really interpret what it is implying.
This line is the error : assessment is default DealState::NotAnEndState.
First you should use MoveToGo::DealState::NotAnEndState and second assessment is default should be in a spec file not here.
If you juste remove this line, there should be no error anymore.

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!

Ruby DataMapper::ImmutableError

get '/watch/:id' do |id|
#results = Twitchtvst.all( :fields => [:Twitchtv ],
:conditions => { :user_id => "#{id}" }
)
#p #results.inspect
#results.each do |result|
puts result.id
end
erb :mystream
end
I get this error message immutable resource cannot be lazy loaded. How do I fix this?
The Error message is:
DataMapper::ImmutableError at /watch/1
Immutable resource cannot be lazy loaded
According to the official documentation:
Note that if you don't include the primary key in the selected columns, you will not be able to modify the returned resources because DataMapper cannot know how to persist them. DataMapper will raise DataMapper::ImmutableError if you're trying to do so nevertheless.
I know that you are not modifying anything here but I think that the same rule applies for lazy loading. So I will suggest to try it like that:
#results = Twitchtvst.all( :fields => [:Twitchtv, :id],
:conditions => { :user_id => "#{id}" }
) ode here
Note the id as an additional field.

Ruby array of objects subtraction by object value

I have two arrays which contain objects of assets, now I want to subtract to get only objects from the first array which the second array doesn't have. I should use "-" right?
Here is my object
class Asset
attr_accessor :id, :url, :text, :img_url, :type, :created_at
def initialize(url, text, type, img_url, created_at)
#id, #url, #text, #img_url, #type, #created_at = "", url, text, img_url, type, created_at
end
def eql?(another_asset)
self_domain = UrlUtils::get_domain(self.url)
another_asset_domain = UrlUtils::get_domain(another_asset.url)
if self_domain == 'youtube' && another_asset_domain == 'youtube'
self_youtube_id = UrlUtils::get_parameter(self.url, "v")
another_asset_youtube_id = UrlUtils::get_parameter(another_asset.url, "v")
return self_youtube_id.eql?(another_asset_youtube_id)
end
return self.url.eql?(another_asset.url)
end
def hash
#created_at.hash + 32 * #url.hash
end
end
The idea is one asset can contain url from youtube which every url might be different but it's the same video, so I have to compare each url with parameter "v" (youtube_id).
And this is my test which is wrong at the moment, because it doesn't do the subtraction correctly.
it "should substract duplicated youtube from mixed assets" do
mixed_assets = Array.new
all_assets = Array.new
google = Asset.new("http://www.google.com", "", "", "", Time.now)
youtube = Asset.new("http://www.youtube.com?v=1", "", "", "", Time.now)
mixed_assets.push(google)
mixed_assets.push(youtube)
another_youtube = Asset.new("http://www.youtube.com?v=1&a=1", "", "", "", Time.now)
all_assets.push(another_youtube)
mixed_assets = mixed_assets - all_assets
mixed_assets.length.should eql 1
mixed_assets[0].url.should eql "http://www.google.com"
end
I'm very new to ruby and I did some research that I should implement "hash" method as well, but I couldn't find any example how to do that.
Thanks.
Array subtraction works via hashes, so you're correct. I couldn't test since I don't know what UrlUtils is, but something similar to the following is likely what you need added to the Asset class:
def hash
domain = UrlUtils::get_domain(self.url)
v = domain == 'youtube' ? UrlUtils::get_parameter(self.url, "v") : ''
domain.hash ^ v.hash
end
You might also need an eql? method. There's a bunch of additional information in this post that you probably will want to look over; it covers this, as well as a bunch of related topics.

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.

Resources