Rspec: error in should eq() - ruby-on-rails-3

Could somebody explain it to me? What is wrong?
1) NetworkController GET #index for staff user locates all network latencies to display
Failure/Error: assigns(:latencies).should eq([#lat1, #lat2, #lat3])
expected: [#<NetworkLatency from_network_id: "BSC", to_network_id: "FZJ", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">, #<NetworkLatency from_network_id: "CSC", to_network_id: "ABC", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">, #<NetworkLatency from_network_id: "CSC", to_network_id: "DEF", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">]
got: [#<NetworkLatency from_network_id: "BSC", to_network_id: "FZJ", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">, #<NetworkLatency from_network_id: "CSC", to_network_id: "ABC", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">, #<NetworkLatency from_network_id: "CSC", to_network_id: "DEF", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">]
(compared using ==)
Diff:
## -1,4 +1,2 ##
-[#<NetworkLatency from_network_id: "BSC", to_network_id: "FZJ", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">,
- #<NetworkLatency from_network_id: "CSC", to_network_id: "ABC", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">,
- #<NetworkLatency from_network_id: "CSC", to_network_id: "DEF", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">]
+[#<NetworkLatency from_network_id: "BSC", to_network_id: "FZJ", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">, #<NetworkLatency from_network_id: "CSC", to_network_id: "ABC", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">, #<NetworkLatency from_network_id: "CSC", to_network_id: "DEF", error_status: nil, percent_packet_lost: 0, min_ping: 54.8, avg_ping: 54.8, max_ping: 54.8, created_at: "2013-07-30 19:09:14", updated_at: "2013-07-30 19:09:14">]
Expected and got are equal to me. So why Rspec reports an error?
EDIT
I use eq
created_at and udated_at are created in schema.rb by t.datetime statements
NetworkLatency objects are created by FactoryGirl as follows
#lat1 = FactoryGirl.create(:network_latency, from_network: #bsc_net, to_network: #fzj_net)
#lat2 = FactoryGirl.create(:network_latency, from_network: #csc_net, to_network: #abc_net)
#lat3 = FactoryGirl.create(:network_latency, from_network: #csc_net, to_network: #def_net)
and then fetched from db by controller using:
NetworkLatency.where(:from_network_id => site_ids).order('from_network_id ASC, to_network_id ASC')

The test results are indicating that the string representation of the two arrays is the same, but that does not mean that the arrays are eq to each other, which requires that each element is eq to the corresponding element in the other array. Do you know that the objects are the same in each array? If not, do you know if the objects have a non-standard definition of ==?

Are those time types actually strings, or are they Ruby's Time type? The latter includes milliseconds and microseconds which are not visible in the string representation; this makes the string representations equal while the times may not be.
For example:
2.0.0p0 :001 > Time.now == Time.now
=> false
2.0.0p0 :002 > Time.now.to_s == Time.now.to_s
=> true
2.0.0p0 :003 > puts "%s, %s" % [ Time.now.tv_usec, Time.now.tv_usec]
247806, 247810

You can override == operator in your NetworkLatency clss:
def ==(other)
self.from_network_id == other.from_network_id && self.to_network_id == other.to_network_id && ...
end

try
assigns(:latencies).should match_array([#lat1, #lat2, #lat3])
or
expect(assigns(:latencies)).to match_array([#lat1, #lat2, #lat3])

I was with the same problem. I solved using timecop gem. It freezes the time then you can do the comparisons without interfering time changes.

Related

Active record where.not returns empty array

Following command
Fight.last.fight_logs.where(item_id: nil)
generates sql:
Fight Load (0.3ms) SELECT "fights".* FROM "fights" ORDER BY "fights"."id" DESC LIMIT $1 [["LIMIT", 1]]
FightLog Load (0.2ms) SELECT "fight_logs".* FROM "fight_logs" WHERE "fight_logs"."fight_id" = $1 AND "fight_logs"."item_id" IS NULL LIMIT $2 [["fight_id", 27], ["LIMIT", 11]]
and returns:
#<ActiveRecord::AssociationRelation [#<FightLog id: 30, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 1, damage: 11.0, created_at: "2017-11-02 16:20:55", updated_at: "2017-11-02 16:20:57">, #<FightLog id: 31, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 20, damage: 3.0, created_at: "2017-11-02 16:20:57", updated_at: "2017-11-02 16:20:57">, #<FightLog id: 33, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 1, damage: 1.0, created_at: "2017-11-02 16:21:40", updated_at: "2017-11-02 16:21:40">, #<FightLog id: 32, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 20, damage: 7.0, created_at: "2017-11-02 16:21:33", updated_at: "2017-11-02 16:21:40">, #<FightLog id: 34, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 1, damage: 12.0, created_at: "2017-11-02 16:21:47", updated_at: "2017-11-02 16:21:48">, #<FightLog id: 35, fight_id: 27, attack: 0, block: 0, item_id: nil, user_id: 20, damage: 14.0, created_at: "2017-11-02 16:21:48", updated_at: "2017-11-02 16:21:48">]>
but
Fight.last.fight_logs.where.not(item_id: 1)
generates sql:
Fight Load (1.0ms) SELECT "fights".* FROM "fights" ORDER BY "fights"."id" DESC LIMIT $1 [["LIMIT", 1]]
FightLog Load (0.8ms) SELECT "fight_logs".* FROM "fight_logs" WHERE "fight_logs"."fight_id" = $1 AND ("fight_logs"."item_id" != $2) LIMIT $3 [["fight_id", 27], ["item_id", 1], ["LIMIT", 11]]
and returns:
#<ActiveRecord::AssociationRelation []>
How it is possible? What i'm doing wrong?
You should specify NULL value in your query since you have it in your database:
Fight.last.fight_logs.where('item_id != ? OR item_id IS NULL', 1)
This is just how SQL works:
select 1 != NULL;
+-----------+
| 1 != NULL |
+-----------+
| NULL |
+-----------+
You can look at this answer to clarify the issue.
Also, I would recommend avoiding using default NULL values in your database, there is nice answer about it. You can simply use default: 0, null: false your case.

ActiveRecord ordering by relation

Loan.includes(:decisions).map{|l| l.decisions.last.try(:smart_rate)}
=> [2, nil, 2, 1, 4, 4, 1, nil, nil, nil, nil, 1, nil, 1, nil, 1, nil, nil, nil, 3, nil, 3, 1, 1, nil, 3, 1, 1, 4, 1, 1, 1, nil, 2, 1, nil, 1, nil, 1, 1, nil, 3, nil, 1, 1, 1, 1, 1, 1, 1]
I'd like to sort Loans by their last decision's smart_rate. I'm not trying to sort the array, but the resulting Loan::ActiveRecord_Relation.
Can this be done via ActiveRecord?
If you have a relationship on Decision then you could do this, the Loans should be in the correct order in the returned hash.
Decision.where(...).includes(:loan).order(:smart_rate).map(&:loan).uniq

How to get specific records with remaining records?

Let's say I've got some records like, And I want to get all cars where type="honda" and doors="4" with all remaining records also
<Car id="1", type="honda", doors="4" created_at: "2015.01.01">
<Car id="2", type="jeep", created_at: "2015.01.01">
<Car id="3", type="mazda", created_at: "2015.01.01">
<Car id="4", type="honda", doors="4" created_at: "2015.01.01">
<Car id="5", type="honda", doors="2" created_at: "2015.01.01">
<Car id="6", type="honda", doors="2" created_at: "2015.01.01">
I want to get this:
<Car id="1", type="honda", doors="4" created_at: "2015.01.01">
<Car id="2", type="jeep", created_at: "2015.01.01">
<Car id="3", type="mazda", created_at: "2015.01.01">
<Car id="4", type="honda", doors="4" created_at: "2015.01.01">
I have this query but it's returning only cars where type="honda" and doors="4" Mainly i don't want to list other types with IS NOT as I'm not sure about their values.
Car.where("type = ? AND doors = ?", "honda", "4")
Use OR operator to select other types than "honda"
Car.where("(type = ? AND doors = ?) OR type <> ?", "honda", "4", "honda")

rails geocoder near_by result join query returns active record relation

the result produced by
locations_by_geocoder = Location.near([params[:latitude], params[:longitude]], params[:radius], :units => :km)
and
locations_by_where = Location.where(:id => [7, 10, 9, 8])
are the same
location_by_geocoder output is
[#<Location id: 7, country: nil, state: nil, city: nil, locality: nil, name: "Coffee On Canvas", latitude: "12.93516201597191", longitude: "77.63097871416265", created_at: "2014-03-29 15:20:34", updated_at: "2014-03-29 15:20:34", address: "#84, S.T.Bed Layout, 4th Block Koramangala", id_location: "cd49dfa966245b12">,
#<Location id: 10, country: nil, state: nil, city: nil, locality: nil, name: nil, latitude: "12.93516201597191", longitude: "77.63097871416265", created_at: "2014-03-30 14:47:03", updated_at: "2014-03-30 15:20:52", address: nil, id_location: "60f7f024d37c74f9">,
#<Location id: 9, country: nil, state: nil, city: nil, locality: nil, name: "Cafe Coffee Day", latitude: "12.93434671368444", longitude: "77.6256083701297", created_at: "2014-03-29 15:41:42", updated_at: "2014-03-29 15:41:42", address: "Mango Suites,", id_location: "8db810b6762fd784">,
#<Location id: 8, country: nil, state: nil, city: nil, locality: nil, name: "Cafe Coffee Day", latitude: "12.932626125142486", longitude: "77.62338638305664", created_at: "2014-03-29 15:31:53", updated_at: "2014-03-29 15:31:53", address: "BPCL RAJAJINAGAR,Dr.RajkumarRoad, Rajajinagar", id_location: "ff71f5a0b4de9e1b">]
location_by_where output is
[#<Location id: 7, country: nil, state: nil, city: nil, locality: nil, name: "Coffee On Canvas", latitude: "12.93516201597191", longitude: "77.63097871416265", created_at: "2014-03-29 15:20:34", updated_at: "2014-03-29 15:20:34", address: "#84, S.T.Bed Layout, 4th Block Koramangala", id_location: "cd49dfa966245b12">,
#<Location id: 8, country: nil, state: nil, city: nil, locality: nil, name: "Cafe Coffee Day", latitude: "12.932626125142486", longitude: "77.62338638305664", created_at: "2014-03-29 15:31:53", updated_at: "2014-03-29 15:31:53", address: "BPCL RAJAJINAGAR,Dr.RajkumarRoad, Rajajinagar", id_location: "ff71f5a0b4de9e1b">,
#<Location id: 9, country: nil, state: nil, city: nil, locality: nil, name: "Cafe Coffee Day", latitude: "12.93434671368444", longitude: "77.6256083701297", created_at: "2014-03-29 15:41:42", updated_at: "2014-03-29 15:41:42", address: "Mango Suites,", id_location: "8db810b6762fd784">,
#<Location id: 10, country: nil, state: nil, city: nil, locality: nil, name: nil, latitude: "12.93516201597191", longitude: "77.63097871416265", created_at: "2014-03-30 14:47:03", updated_at: "2014-03-30 15:20:52", address: nil, id_location: "60f7f024d37c74f9">]
but while using it in a join both produces different results
books = books.joins(:location).merge(locations_by_geocoder)
produces
[#<Book id: 10, created_at: "2014-03-30 14:47:03", updated_at: "2014-03-30 15:20:52">]
query syntax is
SELECT locations.*, 6371.0 * 2 * ASIN(SQRT(POWER(SIN((12.9398981 - locations.latitude) * PI() / 180 / 2), 2) + COS(12.9398981 * PI() / 180) * COS(locations.latitude * PI() / 180) * POWER(SIN((77.6275559 - locations.longitude) * PI() / 180 / 2), 2))) AS distance, CAST(DEGREES(ATAN2( RADIANS(locations.longitude - 77.6275559), RADIANS(locations.latitude - 12.9398981))) + 360 AS decimal) % 360 AS bearing FROM `books` INNER JOIN `locations` ON `locations`.`id` = `books`.`location_id` WHERE (locations.latitude BETWEEN 12.930904883940814 AND 12.948891316059187 AND locations.longitude BETWEEN 77.61832835502157 AND 77.63678344497843 AND 6371.0 * 2 * ASIN(SQRT(POWER(SIN((12.9398981 - locations.latitude) * PI() / 180 / 2), 2) + COS(12.9398981 * PI() / 180) * COS(locations.latitude * PI() / 180) * POWER(SIN((77.6275559 - locations.longitude) * PI() / 180 / 2), 2))) <= '1') ORDER BY distance ASC
where as
books = books.joins(:location).merge(locations_by_where)
produces
[#<Book id: 11, title: "ggn", author: "steven jobs,steve jobs", isbn_10: "1572846933", isbn_13: "1932841660", edition: "1", print: nil, publication_year: 2011, publication_month: "2011", condition: "", value: nil, status: nil, stage: nil, description: "", visits: 38, user_id: 5, prefered_place: nil, prefered_time: nil, created_at: "2014-01-01 08:13:00", updated_at: "2014-03-30 14:47:35", rating: nil, image: nil, publisher: nil, goodreads_id: nil, ext_image_url: nil, pages: nil, language_code: nil, barter_type: nil, location_id: 10, id_book: nil>]
and query syntax is
SELECT `books`.* FROM `books` INNER JOIN `locations` ON `locations`.`id` = `books`.`location_id` WHERE `locations`.`id` IN (7, 10, 9, 8)
even though the output of locations_by_where and locations_by_geocoder are same
both are ActiveRecord::Relation::ActiveRecord_Relation_Location
the issue is connected to near scope in locations.near
https://github.com/alexreisner/geocoder/issues/627#issuecomment-39048249

Getting rows in the same table

I'm trying to get rows in a Table.
Imagine I got two records (t1 and t2). I want to get rows that do not have the t1.start_hour BETWEEN t2.start_hour and t2.finish_hour. I basically want only to get the occurrences that don not have conflict in hours with another one.
This is the table:
create_table "occurrences", :force => true do |t|
t.string "start_hour"
t.string "finish_hour"
t.date "start_date"
t.date "finish_date"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "activity_id"
end
And this is the SQL query I came up so far:
Occurrence.find_by_sql("SELECT * FROM occurrences t1 INNER JOIN occurrences t2 ON (t1.start_hour NOT BETWEEN t2.start_hour and t2.finish_hour)")
It gives me duplicate results. I'm not be able to remove them and get the correct answer.
Thanks for the help in advance.
Example
INPUT
#<Occurrence id: 1, start_hour: "19:00", finish_hour: "20:20", start_date: "2012-05-30", finish_date: "2012-05-30", created_at: "2012-05-30 09:58:19", updated_at: "2012-05-30 09:58:19", activity_id: 1>,
#<Occurrence id: 2, start_hour: "19:30", finish_hour: "20:10", start_date: "2012-05-30", finish_date: "2012-05-30", created_at: "2012-05-30 09:58:19", updated_at: "2012-05-30 09:58:19", activity_id: 2>,
#<Occurrence id: 3, start_hour: "22:00", finish_hour: "23:20", start_date: "2012-05-30", finish_date: "2012-05-30", created_at: "2012-05-30 09:58:20", updated_at: "2012-05-30 09:58:20", activity_id: 3>
OUTPUT
#<Occurrence id: 1, start_hour: "19:00", finish_hour: "20:20", start_date: "2012-05-30", finish_date: "2012-05-30", created_at: "2012-05-30 09:58:19", updated_at: "2012-05-30 09:58:19", activity_id: 1>,
#<Occurrence id: 3, start_hour: "22:00", finish_hour: "23:20", start_date: "2012-05-30", finish_date: "2012-05-30", created_at: "2012-05-30 09:58:20", updated_at: "2012-05-30 09:58:20", activity_id: 3>
The record with the start_hour = 19:30 does not output because is between 19:00 and 20:20 of another one.
EDIT:
I Got the solution:
Occurrence.find_by_sql("SELECT start_hour FROM occurrences WHERE start_hour NOT IN (SELECT t2.start_hour FROM occurrences t1 INNER JOIN occurrences t2 ON ((t1.activity_id <> t2.activity_id AND t2.start_hour BETWEEN t1.start_hour and t1.finish_hour)))")
Thanks for the help
Let assume there are 3 records in table. (I am taking integer in place of datatime as this will be easier)
id start end
1 1 3
2 4 5
3 7 9
When I will try to find the rows were start is not in between start and end, than for id =1 both first row will be true and will come in result. Similarly for row with id =2 (start=4) both rows will qualify (making third row come twice in result) Same will happen for third row and you will end up with six rows.
Its not very clear what you are trying to achieve here, but putting distinct will remove the duplicate.
EDIT: You might consider putting inner joins on start and finish date.
not tested (from memory)
you need to exlude the record itself --> t1.activity_id <> t2.activity_id
left join or you won't get the good ones
where there is no right side
would need to test this :p
SELECT * FROM occurrences t1
left JOIN occurrences t2
ON (t1.activity_id <> t2.activity_id and t1.start_hour BETWEEN t2.start_hour and t2.finish_hour)
where t2.activity_id is null