I'm having an issue with Rails 4's support for Postgresql's ts_range data type. Here is the code that I am trying to persist:
before_validation :set_appointment
attr_accessor :starting_tsrange, :ending_tsrange
def set_appointment
self.appointment = convert_to_utc(starting_tsrange)...convert_to_utc(ending_tsrange)
end
def convert_to_utc
ActiveSupport::TimeZone.new("America/New_York").parse(time_string).utc
end
Basically I set an instance variable for the beginning and end of the appointment ts_range with two strings representing date_times. Before validation it converts them to utc and saves those values to the appointment attribute which should then be persisted. It sets things correctly but when I try to retrieve the record, the appointment attribute is now nil. Why is this code not working as expected?
Figured out the subtle bug in the code. The issue here is with the triple dot range operator. If we get two values that are the exact same time. The triple dot will say include everything from time a up until time b if they are the same exact time, then nothing will be included and the result will be nil. This can be visualized with the code below
(1...1).to_a # []
(1..1).to_a # [1]
So the way to fix this is to not use the triple dot notation when using ranges that can have the same value for a time. Use the double dot notation instead.
Related
I'm pretty new to ruby on rails, so I'm probably missing some syntax. Big picture I am trying to get the value for a specified percentile. Conceptually I am taking my table 'Scores', sorting it, getting the last 'x' values, and then taking the first value. I can't seem to figure out how to pass 'x', which is based on the length of the dataset to the chain.
def get_percentile()
record_count = Scores.count(:id)*0.05
record_threshold = record_count.round()
Score_percentile = Scores.order(:points).last(record_threshold).first().points
return Score_percentile
end
get_percentile
If I just enter .last(20) this works as I expect, so I just don't know how to pass the variable.
Thanks.
You may be passing a 0 into your .last() function with your rounding.
There are a variety of options to make sure you pass at least a 1
[record_threshold, 1].max will give you at least 1. https://apidock.com/ruby/Enumerable/max
Changing .round() to .ceil() rounds up in all instances. https://apidock.com/ruby/Float/ceil
I have a form in a ruby on rails site that takes in a bunch of different parameters. I want to ensure that none of these parameters equal each other IF they are not blank. With how the form is designed, none of these values are required, so some of them can be blank. I can do this with a conditional statement of course, but I wanted to ensure whether or not there is a cleaner way to do so as the conditional statement would be HUGE with all of those checks. Again, if any of the values are equal to each other when (and only when) those values are chosen, I want to return an error that they can not be equal. The values are chosen by collection_select controls, so I'm not seeing any way to filter out duplicates beforehand. Is there a more concise way to compare all of these values?
if ((value1 == value2) && !value2.blank?) ||
((value1 == value3) && !value3.blank?) ||
((value1 == value4) && !value4.blank?) ||
....
((value9 == value10) && !value10.blank?))
Do error stuff
else
Behave normally
end
This is the cleanest way of which I can think, assuming you don't need to know which elements are equal:
I assume the items are in an array (or a collection that can easily be turned into an array). Regardless, somehow land them in this form.
ary = [value1,value2,value3,value4,value5,value6,value7,value8,value9,value10]
Drop the blank items.
ary.reject!(|value| value.blank?)
Find out if there are more elements than unique elements
if ary.length > ary.uniq.length
# Do error stuff
else
# Behave Normally
end
Note:
This approach may be clean to read and highly scalable (an array of 100 elements with this approach is far more efficient than 100 elsifs). However, if your data is not in an easily-array-ready form (i.e. already in an array, hash, or other enumerable) the assignment into an array may not be worth the cost.
Furthermore, as aforementioned, this only returns if values are shared, not which values are shared, or which variables share values.
Additionally, for step 2, if the only values for which you're checking .blank? are nil, you could simply use ary.compact! instead.
First, errors and form / data validations should really be model-layer stuff. So you should consider moving this to a validation on the model, and then ActiveRecord errors + something like simple_form will make error raising / rendering a breeze.
Back to your question - are you using Rails 4? If so, like Connor mentions, simply add a little logic to strip out blank params values in your permit!
class ExampleController < ApplicationController
private
def permitted_params
params.require(:example).permit(:a, :b, :c).reject!(&:blank?)
end
end
Then, your logic becomes simpler and you can just compare values... which at this point I would add to your model layer
class MyModel < ActiveRecord::Base
validate: :must_be_different
private
def must_be_different
# compare value1 with value2 (take advantage of _changed? helper)
end
end
I am having a problem with the calculation of burned calories.
#calories_burned.each do |calories|
json.child! do
json.calories_burned calories.calories_burned(#calories_burned)
json.week_number calories.week_number
end
end
The number of calories burned, I figured method calories_burned
In the method is passed an object with fields exercises_count and week_nubmer.
Calories are computed as: exercises_count * 3.
The problem is, when I return the value received, came back the entire array.
def calories_burned(grouped_by_weeks)
grouped_by_weeks.map { |x| x.exercises_count * 3 }
end
Example of what happens:
[{"calories_burned":[3,4],"week_number":17},{"calories_burned":[3,4],"week_number":22}]
Example of how it should be.
[{"calories_burned":3,"week_number":17},{"calories_burned":4,"week_number":22}]
You are passing the array #calories_burned to the method calories.calories_burned(), which then goes over the whole array, returning the computation you specify.
To achieve what you intended, I think you meant to pass only the current object, which is strange in itself, since it is calories itself, so the minimal effort fix would be:
#calories_burned.each do |calories|
json.child! do
json.calories_burned calories.calories_burned
json.week_number calories.week_number
end
end
class Calories
def calories_burned
exercises_count * 3
end
end
Though I would think hard about changing the naming of your objects, #calories_burned, calories, calories.calories_burned - these names are very confusing and do not convey the meaning of the object they represent. Maybe better names would be #exercise_reports, exercise_report and exercise_report.calories_burned?
I have a Rails model that has a field array_field, which is a serialized text array. I want the combination of this array value and the value of another_field to be unique.
Should be straightforward, no?
class Foo < ActiveRecord::Base
validates_uniqueness_of :array_field, scope: [:another_field]
serialize :filters, Array
end
This doesn't work. However, if I switch them around in the validations,
validates_uniqueness_of :another_field, scope: [:array_field] works as expected.
Can someone explain why this is the case? Is this expected behavior?
The Postgres error for the former setup when array_field's value is nil or [] is this:
PG::SyntaxError: ERROR: syntax error at or near ")"
LINE 1: ...other_field" = 103 AND "foo"."array_field" = ) LIMIT 1
When array_field is [[1, 2], [3, 4, 5]] (a sample multiarray I was using), it's:
PG::UndefinedFunction: ERROR: operator does not exist: text = integer
LINE 1: ...other_field" = 103 AND "foo"."array_field" = 1, 2, 3, 4, 5) LIMIT 1
It seems that Rails doesn't know how to translate the serialized object for this query. Am I missing something or is this a bug?
Edit: This is occurring in Rails 4.0.2.
Second Edit:
Clarification: I understand why this is happening (Rails has custom logic for list queries), and I'm using both a custom validator to manually perform the serialization before validating and a custom serializer to avoid problems with comparisons of Yaml strings (as detailed in my other question here).
At this point I'm mostly just wondering why validates_uniqueness_of treats the primary field differently from the scope fields, and am hoping someone can shed some light.
I can't explain why the validations work one way around, but not the other.
But I think basically your problems are due to the fact that serialize only defines that an attribute is to be serialized using Yaml on save and deserialized upon load.
In other words: the only thing you say by doing serialize :filters, Array is that
when saving a Foo, serialize it's filters attribute using Yaml first,
when loading a Foo from the DB, make sure that the value of the
filters attribute is an Array after deserialization, otherwise raise an exception
It does not affect how queries are constructed. Instead, Rails' usual rules for queries are used. So an array is converted into a comma separated list of numbers. This makes sense for example when constructing a LIKE query. This is the reason why the query fails. The DB field is a string but you're trying to compare it to a list.
I haven't used native PostgreSQL array columns with Rails 4, but my guess is that these issues would solved if you used those instead a serialization-type solution. You get the added benefit of being able to search within the contents of arrays on the DB level.
I have an AR query that returns a hash of events per month, ordered by month
o.events.group("to_char(date,'MM')").order("to_char(date,'MM')").size()
I'm using numeric months in this query as it was the best way I could find to get things in the correct order, and I also need to do some other manipulations on the hash.
Before display the results, I need to convert the numeric months back to words. I added the following to the end of the query
.each_key{ |key| Date::MONTHNAMES[key] }
But i get
TypeError: can't convert String into Integer.
So i tried
.each_key{ |key| Date::MONTHNAMES[key.to_i] }
But the months remain in numeric form
{"01"=>4, "02"=>3.....
How can i manipulate this hash to get
{"January"=>4, "February"=>3.....
Make a new Hash. If you can't, make a new key in the current hash and delete the original key. You can't simply change a key, since key is a local variable in the block, and changing it in no way impacts the contents of the Hash.
This ? :
def number_to_month(number)
(Time.now.beginning_of_year + number.months).strftime("%B")
end
There are ways to generate a new hash but I think you could just convert the strings to month names in your view right before displaying them. Use the code you already wrote inside the block in your question but put it in your view.