Why Rails allows trailing characters in finder methods? (Rails 6) - ruby-on-rails

I can able to find the record with all of the below methods, is there a way to avoid it?
I want to allow only numbers for the integer columns for all models.
User.find(1) -> Works
User.find("1") -> Works
User.find("1trailingstrings") -> Works (i want this to be disabled)
User.find(1) -> Works
User.where(mynumber: "134") -> Works
User.where(mynumber: "134dsadsasd") -> Works (i want this to be disabled)
User.where(mynumber: [134]) -> Works
User.where(mynumber: ["134"]) -> Works
User.where(mynumber: ["134dsadsasd"]) -> Works (i want this to be disabled)

User.find("1trailingstrings") -> Works (i want this to be disabled)
You can consider using a wrapper that delegates to Kernel#Integer under the hood:
(this wrapper is not comprehensive, as #engineersmnky mentioned, find receives *args)
class UserWrapper
def self.find(id)
parsed_id = Integer(id, exception: false)
return unless parsed_id
# or raise an error
User.find(parsed_id)
end
end
UserWrapper.find(raw_id)
Or you can overwrite the .find method on User, e.g:
class User
def self.find(id)
parsed_id = Integer(id, exception: false)
return unless parsed_id
# or raise an error
super(parsed_id)
end
end
User.find(raw_id)
Kernel#Integer parses integers when a string contains only digits:
Why Rails allows trailing strings in finder methods? (Rails 6)
If you are curious about why to_i was chosen, https://discuss.rubyonrails.org/ could be a better place to ask since it receives more attention from Rails contributors in general (personal observation, may be wrong).

Related

How can I customise AttributeSerializerFactory in papertrail?

I am having an issue creating versions for a model that contains datetime columns.
I do not know why. I've already asked a question. See Error on reify a version using ruby PaperTrail
Because I do not find a solution and I've verified that everything else works if I just skip these columns, I would like to solve the issue implementing my own serializer/deserializer.
I can see that papertrails relies on a class called CastAttributeSerializer and that this class uses a factory as shown in the following snippet:
def deserialize(attr, val)
if defined_enums[attr] && val.is_a?(::String)
# Because PT 4 used to save the string version of enums to `object_changes`
val
elsif PaperTrail::RAILS_GTE_7_0 && val.is_a?(ActiveRecord::Type::Time::Value)
# Because Rails 7 time attribute throws a delegation error when you deserialize
# it with the factory.
# See ActiveRecord::Type::Time::Value crashes when loaded from YAML on rails 7.0
# https://github.com/rails/rails/issues/43966
val.instance_variable_get(:#time)
else
AttributeSerializerFactory.for(#klass, attr).deserialize(val)
end
def serialize(attr, val)
AttributeSerializerFactory.for(#klass, attr).serialize(val)
end
If I understand correctly, I should somehow modify the factory AttributeSerializerFactory to introduce my own serializer/deserializer for the class Time (that is failing).
What's the correct way to do it?

Ruby error - Undefined Method

I am try to write a function that will find the items in an array which match the string passed to the function. See code below.
class Island
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}</i>
How i keep getting "undefined method 'filer' for #
I'm not sure what i'm doing wrong.
First let me object against the solution posted by #Sravan :
While it is true - and sometimes even a good solution - to monkey-patch a class, you have to be careful how to do it, because it may become a time bomb:
Ruby evolves, and new versions often add methods to existing classes. This means that if you add a method Array#search, and a new version of Ruby will also add a method of the same name, your new method will SILENTLY override the one in Ruby. You likely won't notice it for long time, until you are using a feature which is supposed to use Rubys Array#search - maybe by using something new in stdlib - and you get weird results. To track down this type of error can be a nightmare. This is exactly the case when you use search as a method name.
Now, how to do it then? Three possibilities:
(1) If you monkey-patch, use at least a method name which is unlikely to become part of the official interface. It might have your project's name as a prefix, or plenty of underscore characters, and so on. Note that this is not 100% foolproof: A later version of Ruby might add under the hood a private method with exactly the same name than the one you were choosing, but of course the odder your name, the less likely this will happen.
(2) If you don't like this idea of using "clumsy" names, you could at least test before defining the new method, whether it already exists, and throw an exception if it doesn't:
class Array
if self.method_defined?(:search)
raise "#{self.class}::search already defined"
else
def search(...)
...
end
end
end
(3) The third possibility is to avoid monkey-patching and keep the method in your Island class. In this case, the method definition would be different:
class Island
def self.filter(array, string)
...
end
end
and it would be called by
Island.filter(myarray, mystring)
UPDATE: Forgot a forth possibility:
(4) You can make Island a subclass of Array. I don't know what else you want to do with your islands, but maybe this is an option worth considering:
class Island < Array
def filter(string)
...
end
end
Of course, when invoking filter, you need to turn your array into an island, before you can use it:
list = Island.new([....])
Following ruby's convention over configuration, you can add/overwrite any method in any class
So, adding a function to array class makes it accessible to all the arrays. So, in this solution.
1) First thing is you have taken the filter function in Island class, instead, you need to take inside Array class since the list is an array.
class Array
def filter(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list = ["sasha","rory","rob","anthony","andre","tariq","kimberly","antoinette"]
list.filter("an"){|i| puts i}
O/P:
anthony
andre
antoinette
2) Since Filter is a keyword as suggested by other answer, take another name for it. Eg: search
class Array
def search(string)
for element in self
if element.include? (string)
yield(element)
end
end
end
end
list.search("an"){|i| puts i}

Make search NOT case sensitive on my rails app

Using this gem: http://filterrific.clearcove.ca/ I have successfully setup search on my app. Here is the scope that is used.
scope :search_by_name, ->(name){ where(name: name) }
Lets say there is a name of 'Jonathon' and you search for 'jon' I would like for it to bring that result up. Also, as it stands now if you search for 'jonathon' (change J to j) it doesn't show the result. You have to have it exactly as the entry.
To stay portable between databases, consider changing your scope to a named method and use AREL matches.
def search_by_name(name)
where(arel_table[:name].matches(name))
end
Though maybe a scope could work:
scope :search_by_name, ->(name){ where(arel_table[:name].matches(name)) }
Since a named scope is the same as a method, especially when you pass it a lambda variable, there is no disadvantage of it being a method instead of a scope.
You may need to prepend or append a % (or both) to the name argument for it to look for anything containing the argument (instead of only exact case-insensitive matches. So perhaps...
def search_by_name(name)
where(arel_table[:name].matches('%' + name + '%'))
end

Using Ruby, can I access the Return variable in the method before it is returned?

I am working on developing a Ruby application and am using puts and otherwise to display variables within methods to trace what is going on. Well, that was changing the "return" of the method, of course. Is there a generalized Ruby variable that allows me to access the current return variable within the method prior to terminating that method? That way, I could access it in puts and then make sure it is set again prior to terminating. I do understand that the last result of the method is what is passed forward. I am specifically looking for a generalized way of accessing that variable whatever it may be in whatever method I am debugging.
I am using Rubymine as my IDE and I do use it for debugging. But, there is something about REPL's like IRB that are just interesting. It's probably the 360 assembler programmer in me, but I am dating myself. How many of you first programmed in 1969? LOL.
Thanks.
One option might be the .tap method. Assuming you had
x = foo
and wanted to know what the return value of foo was from the outside:
x = foo.tap { |r| puts r.inspect }
Remainder of puts r.inspect are written p r, which is valid shorthand syntax for same thing (and by the way, p returns its parameter so you could just literally stick a p in front of returned variables in many cases - a few syntax niceties excepted)
Alternatively, if you want to check inside of foo, and the definition is like this:
def foo
# Do stuff
something_that_is_returned
end
You can add the .tap onto the last statement without affecting the returned value:
def foo
# Do stuff
something_that_is_returned.tap { |r| p r }
end
This works because .tap yields the current object to the block, and returns the original object regardless of the return value of the block. It's quite useful to insert a .tap into a long chain of methods to inspect a middle result.
E.g. You have
x = foo.map {|x| whatever}.select {|x| whatever}
and want to know what select is receiving without re-writing lots of code:
x = foo.map {|x| whatever}.tap { |r| p r }.select {|x| whatever}
If you find yourself doing this a lot, you may wish to define a global method for it:
def tapp
tap do |o|
p o
end
end
To use, just append .tapp to an expression:
something_that_is_returned.tapp
Alternatively, if you want to be a little more expressive, you could define a method
def puts_and_return(r)
puts r
r
end
and then use
puts_and_return (last statement of method)
Why a simple puts does not work for you?
def foo
bar = 1
puts bar
bar
end

Ruby: read info from keyboard

I've recently started learning Ruby on Rails, and I'm trying to write a program that will allow a little bit of user interaction via the keyboard.
In the past, I've mostly only used Java, so I was wondering what Ruby's equivalent to the readKeyboard method is, and how you would use it?
My program is just a simple one that will have lists of students, courses and modules, and will allow the user to register students to a course, and enlist them on modules that are run by that course.
I have a class called 'Module', which currently looks like this:
class Module
# To change this template use File | Settings | File Templates.
##moduleScheme = nil
def initialize(v)
#val = v
end
# Set and get the #val object value
def set (v)
#val = v
end
def get
return #val
end
def addModule
moduleName = Module.new(30)
moduleRefNo = ran(100)
moduleYear(4)
end
def addModuleToScheme
moduleName.moduleScheme = schemeName
end
def removeModuleFromScheme
moduleName.moduleScheme = nil
end
def queryModule
end
end
With the 'queryModule' function, I want the user to be able to specify which module they want to query, by typing in the module's unique identifier. Presumably, I would use 'puts' to indicate that that's what the user is required to do? But then I don't know how I would read whatever the user types on the keyboard into the program. Could someone point me in the right direction?
Ruby (as far as Rails goes) is server-side, so it tends not to be directly involved in the user interaction. You'd begin to handle it on the front-end (html, javascript, etc.)
As far as the logic goes, post human-interaction, you either have that done on frontend as well (Javascript), or if you really need Ruby on Rails to utilize the interactions you occasionally perform AJAX calls (or similar) to Rails sending the relevant information.
However: if the user is directly on the console, you read the input with gets, not puts; puts will print to the command line.

Resources