I'm wondering if there's a convention / best practice for how initialize should be used when building Ruby classes. I've recently built a class as follows:
class MyClass
def initialize(file_path)
#mapped_file = map_file(file_path)
end
def map_file(file_path)
# do some processing and return the data
end
def run
#mapped_file.do_something
end
end
This uses initialize to do a lot of heavy lifting, before methods are subsequently called (all of which rely on #mapped_data).
My question is whether such processing should be handled outside of the constructor, with initialize used simply to store the instances' inputs. Would the following, for example, be preferable?
class MyClass
def initialize(file_path)
#file_path = file_path
end
def run
mapped_file.do_something_else do
etc_etc
end
end
def mapped_file(file_path)
#mapped_file ||= map_the_file_here
end
end
I hope this question isn't considered too opinion based, but will happily remove if it's deemed to be.
So, is there a 'correct' way to use initialize, and how would this fit with the scenarios above?
Any questions or comments, let me know.
As was mentioned in the comments, constructor is usually used to prepare the object, not do any actual work. More than a ruby convention, this a rule of thumb of almost all Object-Oriented languages.
What does "preparing the object" usually entail? Initializing members with default values, assigning passed arguments, calling the initializer of a super-class if such exists, etc.
In your case, this how I would rewrite your class:
class MyClass
def initialize(file_path)
#file_path = file_path
end
def map_file
#mapped_file ||= map_file_here(#file_path)
end
def run
map_file.do_something
end
end
Since run requires the file to be mapped, it always calls map_file first. But the internal map_file_here executes only once.
Related
I apologize up front. I'm going to struggle articulating this question.
TL;DR - I have an object I'm using to call a method in the subclass. That method needs access to a method in a parent class (forcing inheritance...read below). I'm thinking this won't work because I'm instantiating the subclass, so the inheritance won't work right, but I'm not sure. I'm still seeking out documentation. Basically, the method in the parent class is not "seen" the way I'm doing this - NoMethodError is the exception.
I prefer DRY code, as most people do. I usually use compositing in lieu of inheritance in my code, but I think I'm at a point where if I want to keep this DRY, I have to use inheritance (though I could be wrong, so I'm open to suggestions), and so I'm trying it out, which leads me to this question.
Given the following Ruby "pseudo" code or example to kind of demonstrate what I'm trying to accomplish:
module SomeModule
class ParentClass
def initialize
end
def method_i_want_to_use(arg1, *args)
# does all the things
end
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.class.method_i_want_to_use(arg1, arg)
end
end
end
And then in a different file, same module
module SomeModule
class SubClass < ParentClass
def initialize
end
# this isn't working
def my_other_method
# things get done and then
method_i_want_to_use(arg1, args) ## <<=== fails
end
end
end
Yet in another file
module SomeModule
class Thing
def initialize
#my_obj = SubClass.new
end
def my_method
#my_obj.my_other_method
end
end
end
So one important thing I missed. The method method_i_want_to_use is a method that is used all over the place in my code. It just so happens that in this one class, inheritance was NOT originally used because this class is basically atomic with the exception of this one method. So my problem is either I copy the method into this class and use it (but that kinda breaks the DRY principle sorta) or I find a way to share this method between classes.
This gets into OOP design pretty heavily and I am aware of that. One could ask: well, is the inheritance as it currently sits even relevant to the objects in question? Yes...and no. They can be. In short, principally, it works, but frankly, I don't like it. TBH, I almost prefer to just copy the method into the "subclass" and remove the inheritance and be done with it, but DRY -- unless I'm going a little too wild with DRY in this context and I kinda think I am.
Anyway, just curious what folks with more knowledge than I have for me on this. This really is the first time I've dabbled this deeply into inheritance. :)
I'd love pointers on how I can keep from implementing
There are two different methods here:
an instance method:
def method_i_want_to_use(arg1, *args)
# does all the things
end
and a class method:
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.class.method_i_want_to_use(arg1, arg)
end
but what you probably want in this case is
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.new.method_i_want_to_use(arg1, arg)
end
There are a few choices and it depends on what method_i_want_to_use is doing. Is it a separate thing? Then you can call it as a class method ParentClass.method_i_want_to_use inside the SubClass without inheritance.
Another way is to define it in a module and include it
include ModuleX
# and then in your code
method_i_want_to_use(...)
I'd use inheritance if you want to have some kind of common abstraction layer and you expect multiple subclasses to behave the same way. If the classes/objects that need to use method_i_want_to_use have different behaviours then inheritance is not the correct choice. Let's say you have a class that send a request to a 3rd party API and you have a class that does saves records to your db. For some reason you need to use the same piece of code (a method) in both cases, maybe to calculate some value. Using inheritance to include the method would be a mistake, because both classes have different behaviours.
Hope that helps.
After fixing some of the syntax errors and changing the call self.class.method_i_want_to_use to self.new.method_i_want_to_use as Adam also mentioned in his answer, this code seems to work fine.
I did not get any undefined methods until I tried to call SomeModule::ParentClass.method_i_want_to_use(3,4) and that was fixed by the change from class to new. Are you sure your undefined method error was not related to that?
module SomeModule
class ParentClass
def initialize
end
def method_i_want_to_use(arg1, *args)
# does all the things
puts "here #{arg1} , #{args}"
end
def self.method_i_want_to_use(arg1, *args)
arg = args.first unless args.empty?
self.new.method_i_want_to_use(arg1, arg)
end
end
end
module SomeModule
class SubClass < ParentClass
def initialize
end
# this isn't working
def my_other_method(arg1, arg2)
# things get done and then
method_i_want_to_use(arg1, arg2) ## <<=== fails
end
end
end
module SomeModule
class Thing
def initialize
#my_obj = SubClass.new
end
def my_method(arg1,arg2)
#my_obj.my_other_method(arg1, arg2)
end
end
end
SomeModule::Thing.new.my_method(1,2)
SomeModule::ParentClass.method_i_want_to_use(3,4)
prints:
here 1 , [2]
here 3 , [4]
Say I have a parent class:
class Stat
def val
raise "method must be implemented by subclass"
end
end
And a subclass:
class MyStat < Stat
def val
#performs costly calculation and returns value
end
end
By virtue of extending the parent class, I would like the subclass to not have to worry about caching the return value of the "val" method.
There are many patterns one could employ here to this effect, and I've tried several on for size, but none of them feel right to me and I know this is a solved problem so it feels silly to waste the time and effort. How is this most commonly dealt with?
Also, it's occurred to me that I may be asking the wrong questions. Maybe I should't be using inheritance at all but composition instead.
Any and all thoughts appreciated.
Edit:
Solution I went with can be summed up as follows:
class Stat
def value
#value ||= build_value
end
def build_value
#to be implemented by subclass
end
end
Typically I use a simple pattern regardless of the presence of inheritance:
class Parent
def val
#val ||= calculate_val
end
def calculate_value
fail "Implementation missing"
end
end
class Child < Parent
def calculate_val
# some expensive computation
end
end
I always prefer to wrap the complex and expensive logic in its own method or methods that have no idea that their return value will be memoized. It gives you a cleaner separation of concerns; one method is for caching, one method is for computing.
It also happens to give you a nice way of overriding the logic, without overriding the caching logic.
In the simple example above, the memoized method val is pretty redundant. But the pattern it also lets you memoize methods that accept arguments, or when the actual caching is less trivial, maintaining that separation of responsibilities between caching and computing:
def is_prime(n)
#is_prime ||= {}
#is_prime[n] ||= compute_is_prime
end
If you want to keep the method names same and not create new methods to put logic in, then prepend modules instead of using parent/child inheritance.
module MA
def val
puts("module's method")
#_val ||= super
end
end
class CA
def val
puts("class's method")
1
end
prepend MA
end
ca = CA.new
ca.val # will print "module's method" and "class's method". will return 1.
ca.val # will print "module's method". will return 1.
I have a class in Ruby that holds some stuff, I'll call FooBox:
class FooBox
...
end
I have two possible backing-data stores for FooBox called BoxA and BoxB with different characteristics but the same interface:
class BoxA
include Enumerable
def put_stuff(thing)
...
end
end
class BoxB
include Enumerable
def put_stuff(thing)
...
end
end
How can I instantiate a FooBox, and, based on a parameter, decide whether to back it with a BoxA or BoxB implementation? I do not want to pass in the implementation into the constructor; I only want to pass something to determine which kind to use.
class FooBox
def initialize(implementation_choice)
# ???
end
end
I usually do something like this:
class BoxA
def self.match? options
# figure out if BoxA can be used given options
end
end
# Implement BoxB (and other strategies) similarly to BoxA
class FooBox
STRATEGIES = [BoxA, BoxB]
def initialize options
#options = options
end
def strategy
#strategy ||= STRATEGIES.detect { |strategy| strategy.match? #options }
end
end
This keeps the responsibility of “knowing” if the strategy is able to be used within the strategy itself (rather than making the context class monolithic), and then just picks the first one in the list that says it can work.
I’ve used this pattern (and similar variations for slightly different problems) several times and have found it very clean.
The simple solution is create a mapping for the strategy's type and strategy class, just like #Andrew Marshall's solution
But to be better I would considering two things:
The strategies' holder (here is the FooxBox ) now need to know every box implenentation, and hard-coding their names to itself; this is not a flexiable
approach, considering one day you want to add another strategy, go to the code and add it? With ruby we can do it with a 'self registering' easily.
You don't want to strategies holder will return implementation wildly, I mean both 'BoxA' and 'BoxB' or someday's 'BoxXYZ' should belong to same strategy
concept, in Java, it maybe means all of them should implemente an interface, with ruby we generally do it with include SomeMoudle
In my application I use the following solution(just demo)
module Strategies
def self.strategies
##strategies ||= {}
end
def self.strategy_for(strategy_name)
##strategies[strategy_name]
end
end
module Strategy
def self.included(base)
base.class_eval do
def self.strategy_as(strategy_name)
Strategies.strategies[strategy_name] = self
end
end
end
end
class BoxA
include Strategy
strategy_as :box_a
def do_stuff
puts "do stuff in BoxA"
end
end
class BoxB
include Strategy
strategy_as :box_b
def do_stuff
p "do stuff in BoxB"
end
end
## test
Strategies.strategy_for(:box_a).new.do_stuff
Strategies.strategy_for(:box_b).new.do_stuff
If you want to detect strategy with match block, you can change strategy_as to accept a block. then use Strategies.strategy_for{...}.new.do_stuff
Can someone explain the difference between initializing "self" and having #variables when defining classes?
Here's an example
class Child < Parent
def initialize(self, stuff):
self.stuff = stuff
super()
end
end
So in this case, wouldn't I be able to replace self.stuff with #stuff? What's the difference? Also, the super() just means whatever is in the Parent initialize method the Child should just inherit it right?
In general, no, self.stuff = stuff and #stuff = stuff are different. The former makes a method call to stuff= on the object, whereas the latter directly sets an instance variable. The former invokes a method which may be public (unless specifically declared private in the class), whereas the latter is always setting a private instance variable.
Usually, they look the same because it is common to define attr_accessor :stuff on classes. attr_accessor is roughly equivalent to the following:
def stuff
#stuff
end
def stuff=(s)
#stuff = s
end
So in that case, they are functionally identical. However, it is possible to define the public interface to allow for different results and side-effects, which would make those two "assignments" clearly different:
def stuff
#stuff_called += 1 # Keeps track of how often this is called, a side effect
return #stuff
end
def stuff=(s)
if s.nil? # Validation, or other side effect. This is not triggered when setting the instance variable directly
raise "Argument should not be nil"
end
#stuff = s
end
You actually can't use self.stuff= unless you specifically create an attr_writer for modifying that value.
In fact, these are equivalent:
class Child
attr_writer :stuff
end
class Child
def stuff=(val)
#stuff = val
end
end
It is more common to use an attr_writer if that is the functionality you want, rather than the explicit method. But you will often use an explicit method if you want to perform extra error checking or change the way the assignment works.
To your question of when to use #stuff = and when to use self.stuff =, I would use the former if you only have simple assignments and if your class is simple, and would move towards the latter if your requirements might become more complicated. There are many other reasons too, but it's more a matter of style than anything else.
I'm trying to learn ruby by building a basic Campfire bot to screw around with at work. I've gotten pretty far (it works!) and learned a lot (it works!), but now I'm trying to make it a bit more complex by separating the actions to be performed out into their own classes, so that they can be easier to write / fix when broken. If you're interested in seeing all the (probably crappy) code, it's all up on GitHub. But for the sake of this question, I'll narrow the scope a bit.
Ideally, I would like to be able to create plugins easily, name them the same as the class name, and drop them into an "actions" directory in the root of the project, where they will be instantiated at runtime. I want the plugins themselves to be as simple as possible to write, so I want them all to inherit some basic methods and properties from an action class.
Here is action.rb as it currently exists:
module CampfireBot
class Action
#handlers = {}
def initialize(room)
#room = room
end
class << self
attr_reader :handlers
attr_reader :room
def hear(pattern, &action)
Action.handlers[pattern] = action
end
end
end
end
Where #room is the room object, and #handlers is a hash of patterns and blocks. I kind of don't understand why I have to do that class << self call, but that's the only way I could get the child plugin classes to see that hear method.
I then attempt to create a simple plugin like so (named Foo.rb):
class Foo < CampfireBot::Action
hear /foo/i do
#room.speak "bar"
end
end
I then have my plugins instantiated inside bot.rb like so:
def load_handlers(room)
actions = Dir.entries("#{BOT_ROOT}/actions").delete_if {|action| /^\./.match(action)}
action_classes = []
# load the source
actions.each do |action|
load "#{BOT_ROOT}/actions/#{action}"
action_classes.push(action.chomp(".rb"))
end
# and instantiate
action_classes.each do |action_class|
Kernel.const_get(action_class).new(room)
end
#handlers = Action.handlers
end
The blocks are then called inside room.rb when the pattern is matched by the following:
handlers.each do |pattern, action|
if pattern.match(msg)
action.call($~)
end
end
If I do puts #room inside the initialization of Action, I see the room object printed out in the console. And if I do puts "foo" inside Foo.rb's hear method, I see foo printed out on the console (so, the pattern match is working). But, I can't read that #room object from the parent class (it comes out as a nil object). So obviously I'm missing something about how this is supposed to be working.
Furthermore, if I do something to make the plugin a bit cleaner (for larger functions) and rewrite it like so:
class Foo < CampfireBot::Action
hear /foo/i do
say_bar
end
def say_bar
#room.speak "bar"
end
end
I get NoMethodError: undefined method 'say_bar' for Foo:Class.
The definition of hear can be pulled out of the class << self block and changed to:
def self.hear(pattern, &action)
Action.handlers[pattern] = action
end
to yield the exact same result. That also immediately explains the problem. hear Is a class method. say_bar is an instance method. You can't call an instance method from a class method, because there simply isn't an instance of the class available.
To understand the class << self bit, you'll have to do your own reading and experiments: I won't try to improve on what has already been said. I'll only say that within the class << self .. end block, self refers to the eigenclass or metaclass of the CampfireBot::Action class. This is the instance of the Class class that holds the definition of the CampfireBot::Action class.