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}
Related
I'm working with a massive legacy code base, so I am looking for advice concerning this particular issue, please, not suggestions of better high-level implementations.
A simplified version of what I'm working with:
class Order < ActiveRecord::Base
has_many :line_items
#other stuff
def balance
#some definition
end
end
class LineItem < ActiveRecord::Base
belongs_to :order
#other stuff
end
module Concerns
module LineItems
module Aggregates
extend ActiveSupport::Concern
#stuff
def balance
#some other definition
end
end
end
end
Order has a method called 'balance,' and a module of LineItem also has a method called 'balance.' It seems that most of the time (in most places in the code base), when specific_line_item.balance is called, it used the method definition under the LineItem module, but there are a couple of places where it instead calls the method from Order.
Is there any way in Ruby/Rails to specify on method call which of these two I'd like to use? OR is there probably something else going on here because Ruby doesn't have method overloading, so the problem I'm describing here isn't possible?
All relevant cases where either method is called are coming from a line_item (i.e. specific_line_item.balance), so I would think it would always choose the method closer to home, rather than making the associative jump and calling Order's 'balance' method without being told to.
EDIT:
Thanks for the responses! It seems I wasn't clear enough with my question. I understand the difference between
Order.first.balance
and
LineItem.first.balance
and that the balance method being called is the one defined within the class for that object. In the situation I'm describing, I observed, in the actual live app environment, that at a place in the code where
LineItem.find(some_id).balance
was called it output not the result that would be computed by the LineItem 'balance' method, but the one from the Order class.
So I had hoped to learn that there's some ruby quirk that might have an object call an associate's method of the same name under some conditions, rather than it's own. But I'm thinking that's not possible, so there's probably something else going on under the covers specific to this situation.
Firstly, ActiveRecord::Concern can change a lot of behaviour and you've left out a lot of code, most crucially, I don't know where it's being injected, but I can make an educated guess.
For a Concern's methods to be available a given object, it must be include'd in the object's class's body.
If you have access to an instance of the Order object, at any point you can call the balance method:
order = Orders.last # grab the last order in your database
order.balance # this will call Order#balance
And if you have the Order then you can also get the LineItem:
order.line_items.first.balance # should call the Concerns:: LineItems::Aggregates#balance
You can open up a Rails console (with rails console) and run the above code to see if it works as you expect. You'll need a working database to get meaningful orders and balances, and you might need to poke around to find a completed order, but Ruby is all about exploration and a REPL is the place to go.
I'd also grep (or ag or ack) the codebase looking for calls to balance maybe doing something like grep -r "(^|\s)\w+\.balance" *, what you want to look for is the word before .balance, that is the receiver of the "balance" message, if that receiver is an Order object then it will call Order#balance and if it is a LineItem object then it will call Concerns:: LineItems::Aggregates#balance instead.
I get the feeling you're not familiar with Ruby's paradigm, and if that's the case then an example might help.
Let's define two simple Ruby objects:
class Doorman
def greet
puts "Good day to you sir!"
end
end
class Bartender
def greet
puts "What are you drinking?"
end
end
Doorman and Bartender both have a greet method, and which is called depends on the object we call greet on.
# Here we instantiate one of each
a_doorman = Doorman.new
a_bartender = Bartender.new
a_doorman.greet # outputs "Good day to you sir!"
a_bartender.greet # outputs "What are you drinking?"
We're still using a method called greet but the receiver is what determines which is called.
Ruby is a "message passing language" and each "method" is not a function but it's a message that is passed to an object and handled by that object.
References
How to use concerns in Rails 4
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
http://guides.rubyonrails.org/command_line.html#rails-console
I have a model called Organizations that contains fields for its address. In the model I have the statement before_save { self.address_line_1 = address_line_1.titleize } and just realized this is changing addresses with PO Box to Po Box.
Another example: I also have a standard Users model with first name/last name. Titleize will change a person's first name from TJ to Tj. Or, if their last name is hyphenated it will go from Smith-Jones to Smith Jones.
With the PO box I would know the exception ahead of time, but not for user's names. Is there any way to allow for these exceptions at all while still having the core titlsize functionality?
I would recommend trying to avoid changing the semantics of titlelize, though, to avoid issues later when you might expect it, in another part of the same application, to do what it's really intended to do. Since you're looking for some fairly specialized functionality for titleize I'd create a new, similar method which you could monkey patch into the String class, as above, called something like, abook_titleize (address book titleize):
class String
def abook_titleize
if allow_titleize(self)
titleize
else
# Check for other behaviors, such as if "self" is all consonants
# or if self is found in a predetermined list of acronyms,
# perhaps return self.upcase
self.upcase
end
end
private
def allow_titleize(s)
# Write some code here that determines if you want this string
# to be titleized and return true if so, otherwise false
end
end
Or something like that. You could make this as simple or as elaborate as you wish. If you really want to change titleize bahavior itself (again, I wouldn't recommend), then:
class String
:alias_method :old_titleize, :titleize
def titleize
if allow_titleize(self)
old_titleize
else
...
I was implementing a form that includes a hard-coded dropdown for a collection and I was wondering what would be the best solution, I know both ways exposed below work, still I did as follows:
class Example
# Options for Example.
self.options
[ 'Yes', 'No', 'Not sure' ]
end
end
which is called by Example.options, but I know it is possible to do as follows as well:
class Example
# Options for Example.
OPTIONS = [ 'Yes', 'No', 'Not sure' ]
end
that would be called with Example::OPTIONS.
The question is, is any of these the good way or it just doesn't matter at all?
The latter is better. If it were a method, a new array and new strings will be created every time it is called, which is a waste of resource.
TL;DR: It depends. Are the values meant to be used outside the class? Could they ever become dynamic? Could they change for subclasses?
As #sawa wrote, the downside of the method (written this way) is that a new array and strings are created each time.
A better way to write it would be:
class Example
def self.options
#options ||= ['Yes', 'No', 'Not sure']
end
end
The array is stored in the instance variable #options, to avoid creating a new array each time.
Written this way, the method is very similar to the constant.
One key difference is if Example is subclassed, it will be more natural to refine the options method than the constant OPTIONS:
class Parent < Example
def self.options
#options ||= [*super, 'Extra']
end
end
To do something similar with constants is difficult. Imagine that your list of options is used in a class method, this would look like:
class Example
OPTIONS = ['Yes', 'No', 'Not sure']
def self.foo(arg)
puts "Available options:",
self::OPTIONS # The self:: is needed here
# ...
end
end
class Parent < Example
OPTIONS = [*superclass::OPTIONS, 'Extra']
end
The tricky thing about constants, is that self::OPTIONS and OPTIONS are not the always same, while self.options and options are the same. Constants are usually used without specifying the scope (e.g. OPTIONS instead of self::OPTIONS) and inheritance will simply not work in that case.
Note that the method gives you the opportunity to make the result dynamic (i.e. return different results depending on other circumstances) without changing the API.
Final note: I'd recommend calling freeze on your array, to avoid anyone modifying it.
What I usually do is have a mix of above-mentioned techniques:
class Player
JURISDICTIONS = %i(de uk ru)
def self.jurisdictions
JURISDICTIONS
end
end
It has few advantages:
It provides a clean interface, encapsulating a constant (you call Player.jurisdictions instead of Player::JURISDICTIONS).
Additional logic can be added later just by altering method.
The method can be stubbed in tests.
IMHO, performance does not matter here.
Update:
Constant can bee hidden using private_constant method (http://ruby-doc.org/core-2.3.0/Module.html#method-i-private_constant)
To further refine Artur's suggestion I would go with a class variable in order to hide visibility of the constant.
class Player
##jurisdictions = %i(de uk ru)
def self.jurisdictions
##jurisdictions
end
end
I trying to learn tdd using RSpec. I took this example from a cheat sheet I found online and am a bit confused as to how I would implement it. To add MovieList.new is automatic but how would I go about adding a parameter when it is already handled with ActiveRecord. And then to add the 'forward' method as well.
describe "forward" do
it "should jump to a next movie" do
next_movie = MovieList.new(2).forward
next_movie.track_number.should == 2
end
end
If this is a test for a MovieList class, create a class called MovieList.
Then in your constructor for that class, make sure it takes in a parameter called track_number, in your test that's the 2.
Then create a method called forward to do whatever you need it to do?
Here's a good example of where I'm going with this:
http://rspec.info/
This may sound ambiguous, but so was the question.
EDIT:
This is a rough idea of how to create a new MovieList class and initialize it with a parameter called track_number.
def MovieList
attr_accessor :track_number
def initialize(track_number)
#track_number = track_number
end
# You can define all your class methods below, you
# can start with forward.
def forward
# do something...
end
end
movie = Movie.new(:track_number => 2)
movie.forward
I am not sure what forward does in your example because you seem to be initializing track_number to 2 then calling forward. I would have expected track_number to increment but your test is checking to see if it's 2 still.
Note, I don't believe you need to change your constructor to take the parameter as long as you pass it in as a hash (the single member hash is implied in my example)...can someone verify or refute this last assertion?
Something like this:
class Category
SOME_CATEGORY = find_by_name("some category")
end
Category::SOME_CATEGORY
tried without a problem, but want to know if it is a bad idea, and the reasons if any..
thanks
If you don't want to hit the database each time you'll have to cache the model. There are several ways to do this, but one quick way is using Memoization. This was introduced in Rails 2.2.
class Category < ActiveRecord::Base
class << self
extend ActiveSupport::Memoizable
def named(name)
find_by_name(name)
end
memoize :named
end
end
Use it like this.
Category.named("some category") # hits the database
Category.named("some category") # doesn't hit the database
The cache should stay persistent across requests. You can reset the cache by passing true as the last parameter.
Category.named("some category", true) # force hitting the database
What do you want to do?
Maybe:
class Category
def self.some_category
Category.find_by_name("some category")
end
end
So you can call:
Category.some_category
=> <Category#2....>
It's not a terrible idea, but it's not really a good one either. It doesn't really fall in line with the way Rails does things. For one thing, you'll end up with a lot of ugly constant code. Too many ALL_CAPS_WORDS and your Ruby starts to look like C++. Bleah.
For another, it's inflexible. Are you going to make one of these constants for every category? If you add a new category two months from now, will you remember to update your Rails code, add a new constant, redeploy it and restart your server?
If it's important to you to be able to access categories very easily, and not repeat DB queries, here's a bit of metaprogramming that'll automatically look them up and create static methods like Lichtamberg's for you on first access:
def self.method_missing(category, *args) # The 'self' makes this a class method
#categories ||= {}
if (#categories[category] = find_by_name(category.to_s))
class_eval "def self.#{category.to_s}; #categories[#{category}]; end"
return #categories[category]
end
super
end
With this method in place, whenever you first call Category.ham, it'll create a class method that returns the value of find_by_name("ham") -- so that neither the query nor method_missing() runs again the next time you call it. This is pretty much the way the OpenStruct class works, BTW; look it up in the Pickaxe book if you want to learn more.
(Of course you'll still have the risk that, because these are all memoized, your Rails app won't reflect any changes you make to your category objects. This makes the assumption that changes won't happen or don't really matter. It's up to you to determine whether that assumption is valid for your app. You could always put an after_update callback in your code that resets ##categories if that's a problem; but at that point this starts to get complicated.)