I have a class with a public method, for example:
class CsvParse
def initialize(csv_file)
#csv_file = csv_file
end
def parse
...
end
end
csv_parse = CsvParse.new('csv_file.csv')
csv_parse.parse
But the design of the class can be like this too:
class CsvParse
def initialize(csv_file)
#csv_file = csv_file
parse
end
private
def parse
...
end
end
csv_parse = CsvParse.new('csv_file.csv')
What is the best practice?
In this particular case there is a helper method exposed, that parses file to it’s csv representation (whatever it is.)
Semantically the best call would be:
csv_parse = CsvParser.parse 'csv_file.csv'
So, I would go with declaring constructor private, preventing these objects from being directly created:
class CsvParser
def initialize ...
end
private_class_method :new
def self.parse *args
CsvParser.send(:new, *args).send(:parse)
end
private
def parse ...
end
Normally you should not start parsing on initialization - according to the single responsibility principle, a method should do one single thing.
Related
From an academic / "for interests sake" (best practice) perspective:
In a Ruby class, I want to provide a static method that calls instance methods of the class. Is this considered an acceptable way of doing it, or is there is a "better" way?
class MyUtil
def apiClient
#apiClient ||= begin
apiClient = Vendor::API::Client.new("mykey")
end
end
def self.setUpSomething(param1, param2, param3=nil)
util = self.new() # <-- is this ok?
util.apiClient.call("foo", param2)
# some logic and more util.apiClient.calls() re-use client.
end
end
And then I can use this lib easily:
MyUtil.setUpSomething("yes","blue")
vs
MyUtil.new().setupUpSomething()
# or
util = MyUtil.new()
util.setUpSomething()
The environment is sys admin scripts that get executed in a controlled manner, 1 call at a time (i.e. not a webapp type of environment that could be subjected to high load).
Personally I would probably do something like:
class MyUtil
API_KEY = "mykey"
def apiClient
#apiClient
end
def initialize
#apiClient = Vendor::API::Client.new(API_KEY)
yield self if block_given?
end
class << self
def setUpSomthing(arg1,arg2,arg3=nil)
self.new do |util|
#setup logic goes here
end
end
def api_call(arg1,arg2,arg3)
util = setUpSomthing(arg1,arg2,arg3)
util.apiClient.call("foo", param2)
#do other stuff
end
end
end
The difference is subtle but in this version setUpSomthing guarantees a return of the instance of the class and it's more obvious what you're doing.
Here setUpSomthing is responsible for setting up the object and then returning it so it can be used for things, this way you have a clear separation of creating the object and setting the object up.
In this particular case you likely need a class instance variable:
class MyUtil
#apiClient = Vendor::API::Client.new("mykey")
def self.setUpSomething(param1, param2, param3=nil)
#apiClient.call("foo", param2)
# some logic and more util.apiClient.calls() re-use client.
end
end
If you want a lazy instantiation, use an accessor:
class MyUtil
class << self
def api_client
#apiClient ||= Vendor::API::Client.new("mykey")
end
def setUpSomething(param1, param2, param3=nil)
apiClient.call("foo", param2)
# some logic and more util.apiClient.calls() re-use client.
end
end
end
Brett, what do you think about this:
class MyUtil
API_KEY = 'SECRET'
def do_something(data)
api_client.foo(data)
end
def do_something_else(data)
api_client.foo(data)
api_client.bar(data)
end
private
def api_client
#api_client ||= Vendor::API::Client.new(API_KEY)
end
end
Usage
s = MyUtil.new
s.do_something('Example') # First call
s.do_something_else('Example 2')
I have a ToDo list program I'm writing for practice. My problem is that by separating concerns and making each list be a class while each task is also a class, I would like to be able to call the name of the list which the new task is being added to without having to pass the list name into the task class (either upon creation or later):
class HigherClass
def initialize
#higher_class_variable = unique_value
#instance_of_lower_class #keep variable empty for now
end
end
class LowerClass
def intitialize
#lower_class_variable = unique_value #this should be the same unique value as above
end
end
instance_of_higher_class = HigherClass.new
instance_of_higher_class.#instance_of_lower_class = LowerClass.new
instance_of_higher_class.#instance_of_lower_class.#lower_class_variable
#this should return the unique_value from the HigherClass instance
If you want to access attributes:
attr_reader :lower_class_variable
Then you can just do this:
#instance_of_lower_class.lower_class_variable
Same principle applies here for higher class:
class HigherClass
attr_writer :instance_of_lower_class
end
Then:
instance_of_higher_class.instance_of_lower_class = LowerClass.new
That all seems rather clunky considering you can do this:
class HigherClass
attr_accessor :unique
def initialize
#unique = unique_value
end
end
class LowerClass < HigherClass
def initialize
# Call superclass initialization
super
# Anything else you want here.
end
def something_using_unique
call_method(unique)
end
end
I was wondering how I could call a method in an instance of a class in another class.
This is what I came up with
class ClassA
def method
return "This is a method_from_class_A"
end
end
class ClassB
def initialize
#method_from_class_A=instance.method
end
def method_calls_method
#method_from_class_A
end
end
instance=ClassA.new
instance2=ClassB.new
puts instance2.method_calls_method
But I get this error:
Testing.rb:9:in initialize': undefined local variable or method
instance' for # (NameError) from
Testing.rb:19:in new' from Testing.rb:19:in'
How could I fix it?
Thank you for your response.
From your description this seems to be what you're going for:
class ClassB
def initialize
#instance_of_class_a = ClassA.new
end
def method_calls_method
#instance_of_class_a.method
end
end
Or to pass in the ClassA instance (this is called dependency injection):
class ClassB
def initialize(class_a_instance)
#instance_of_class_a = class_a_instance
end
def method_calls_method
#instance_of_class_a.method
end
end
instance_a = ClassA.new
instance_b = ClassB.new(instance_a)
puts instance_b.method_calls_method
Another Option would be to take a look at class methods: https://rubymonk.com/learning/books/4-ruby-primer-ascent/chapters/45-more-classes/lessons/113-class-variables
So in your code it would look similar to this:
class ClassA
def self.method
return "This is a method_from_class_A"
end
end
class ClassB
def method_calls_method
ClassA.method
end
end
instance=ClassB.new
puts instance.method_calls_method
*Notice the self. in ClassA to signify a class method. This is similar to a static method in other languages.
According to wikipedia: https://en.wikipedia.org/wiki/Method_(computer_programming)#Static_methods
Class(static) methods are meant to be relevant to all the instances of a class rather than to any specific instance.
You see class methods used a lot in the ruby Math class:
http://ruby-doc.org/core-2.2.2/Math.html
For example taking a square root of a number in is done by using the class method Math.sqrt. This is different from an instance method which would look like object.method instead Class.method. There are a lot of resources and tutorials out that explains this concept in more detail and probably clearer.
I'm using Ruby's metaprogramming methods creating a bunch of methods within a class. Within the class OmekaItem there are a bunch of methods of this form dc_title and dc_subject, and there are a bunch of methods of this form itm_field1 and itm_field2. I'd like to group those methods better. Ideally, given an instance of the class named item, I'd like call the methods this way:
item.dublin_core.title
item.item_type_metadata.field
and so on. Is there a way to do this?
This question has the code I'm working with.
Would something like the following work for you?
class OmekaItem
class DublinCore
def initialize(omeka_item)
#omeka_item = omeka_item
end
def title
#omeka_item.dc_title
end
def subject
#omeka_item.dc_subject
end
end
class ItemTypeMetadata
def initialize(omeka_item)
#omeka_item = omeka_item
end
def field1
#omeka_item.itm_field1
end
def field2
#omeka_item.itm_field2
end
end
def dublin_core
#dublin_core ||= DublinCore.new(self)
end
def item_type_metadata
#item_type_metadata ||= ItemTypeMetadata.new(self)
end
end
The methods on DublinCore and ItemTypeMetadata could be dynamically generated using define_method as appropriate.
I want a method to be executed every 5 minutes, I implemented whenever for ruby (cron). But it does not work. I think my method is not accessible.
The method I want to execute is located in a class. I think I have to make that method static so I can access it with MyClass.MyMethod. But I can not find the right syntax or maybe I am looking in the wrong place.
Schedule.rb
every 5.minutes do
runner "Ping.checkPings"
end
Ping.rb
def checkPings
gate = Net::Ping::External.new("10.10.1.1")
#monitor_ping = Ping.new()
if gate.ping?
MonitorPing.WAN = true
else
MonitorPing.WAN = false
end
#monitor_ping.save
end
To declare a static method, write ...
def self.checkPings
# A static method
end
... or ...
class Myclass extend self
def checkPings
# Its static method
end
end
You can use static methods in Ruby like this:
class MyModel
def self.do_something
puts "this is a static method"
end
end
MyModel.do_something # => "this is a static method"
MyModel::do_something # => "this is a static method"
Also notice that you're using a wrong naming convention for your method. It should be check_pings instead, but this does not affect if your code works or not, it's just the ruby-style.
Change your code from
class MyModel
def checkPings
end
end
to
class MyModel
def self.checkPings
end
end
Note there is self added to the method name.
def checkPings is an instance method for the class MyModel whereas def self.checkPings is a class method.
Instead of extending self for the whole class, you can create a block that extends from self and define your static methods inside.
you would do something like this :
class << self
#define static methods here
end
So in your example, you would do something like this :
class Ping
class << self
def checkPings
#do you ping code here
# checkPings is a static method
end
end
end
and you can call it as follows : Ping.checkPings
There are some ways to declare a static method in RoR.
#1
class YourClassName
class << self
def your_static_method (params)
# Your code here
end
end
end
#2
class YourClassName
def self.your_status_method
// Your code here
end
end
You cannot have static methods in Ruby. In Ruby, all methods are dynamic. There is only one kind of method in Ruby: dynamic instance methods.
Really, the term static method is a misnomer anyway. A static method is a method which is not associated with any object and which is not dispatched dynamically (hence "static"), but those two are pretty much the definition of what it means to be a "method". We already have a perfectly good name for this construct: a procedure.