Best convention for using ARGV with class in Ruby - ruby

situation
I'm learning Ruby's class and constant now.
I thought example code for my class, and decide to make class for practice.
I will use ARGV for argument of my script.
But i'm wondering for right convention for using ARGV with class.
My question is,
Where should i place my code which access to ARGV? I thought several example for structure. (A and B)
Which one is better for convention?
Is this right convention for using ARGV inside of class like example A?
A
class ExampleClass
CONSTANT = ARGV[0]
def example_function
puts CONSTANT
end
end
ex = ExampleClass.new
ex.example_function
B
class ExampleClass
def example_function
puts CONSTANT
end
end
CONSTANT = ARGV[0]
ex = ExampleClass.new
ex.example_function

I would do:
class ExampleClass
def initialize(args)
#args = args
end
def example_function
#args
end
end
ex = ExampleClass.new(ARGV[0])
ex.example_function

Related

Ruby class, arguments being passed around methods, instance variable?

I'm having this problem which I think it might be a code smell, I have a class that receives an argument in its initialiser and contains one public and several private methods - everything normal. Example:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
foo_1(examples)
end
private
def foo_1(examples)
foo_2(examples) ? 1 : 2
end
def foo_2(examples)
examples.size > #a
end
end
My problem here, is 'examples' that is received by the public method being carried around over and over the private methods, it doesn't look pretty and it seems like a code smell, what's the best approach here? Make it an instance variable inside the public method?
Thanks
Yes, this may be considered a code smell if the number of private methods accepting examples is bigger than 1-2.
One thing to consider would be to extract a class to represent the rule here.
For example:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
size_rule_applies?(examples) ? 1 : 2
end
private
def size_rule_applies?(examples)
SizeRule.new(#a, examples).apply?
end
class SizeRule
def initialize(a, examples)
#a = a
#examples = examples
end
def apply?
#examples.size > #a
end
end
end
I wouldn't make the examples an instance variable of the Foo class as there's a risk it would persist in memory between the calls to that object. I've seen bugs like that.
If examples changes dynamically then making it instance variable is not an option. You'll either need to instantiate Foo for every examples or you'll end up having mutable Foo where examples itself is changing, which is also not good.
Your code looks fine. The only thing that concerns me is that one method depends on another. It's usually not a big deal, but this would look better to me:
def apply?(examples)
foo_1(examples) && foo_2(examples)
end
Another option is to use block:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
foo_1 { examples }
end
private
def foo_1
v = foo_2 { yield.size }
v ? 1 : 2
end
def foo_2
yield > #a
end
end
Since the class Foo does nothing else presently than just evaluate .apply?(examples) I will advice examples be added to the initializer and made an instance variable. This is simpler, more efficient and more obvious.
class Foo
def initialize(a, examples)
#a = a
#examples = examples
end
def apply?
foo_1
end
private
def foo_1
foo_2 ? 1 : 2
end
def foo_2
#examples.size > #a
end
end

Is it possible to bind a parameter to a (unary) Proc object?

(Crossposting note: I have asked this already at the Ruby Forum one week ago, but did not get any response yet).
Here is a (very) simplified, working version of what I have so far:
# A class S with two methods, one which requires one parameter, and
# one without parameters.
class S
def initialize(s); #ms = s; end
def s_method1(i); puts "s_method1 #{i} #{#ms}"; end
def s_method2; puts "s_method2 #{#ms}"; end
end
# A class T which uses S, and "associates" itself to
# one of the both methods in S, depending on how it is
# initialized.
class T
def initialize(s, choice=nil)
#s = S.new(s)
# If choice is true, associate to the one-parameter-method, otherwise
# to the parameterless method.
#pobj = choice ? lambda { #s.s_method1(choice) } : #s.method(:s_method2)
end
# Here is how I use this association
def invoke
#pobj.call
end
end
In this example, depending on how T is constructed, T#invoke calls
either S#s_method1 or S#S_method2, but in the case of calling
S#s_method1, the parameter to s_method1 is already fixed at creation
time of the T object. Hence, the following two lines,
T.new('no arguments').invoke
T.new('one argument', 12345).invoke
produce the output
s_method2 no arguments
s_method1 12345 one argument
which is exactly what I need.
Now to my question:
In the case, where choice is nil, i.e. where I want to invoke the
parameterless method s_method2, I can get my callable object in an
elegant way by
#s.method(:s_method2)
In the case where choice is non-nil, I had to construct a Proc object
using `lambda. This not only looks clumsy, but also makes me feel a bit
uncomfortable. We have a closure here, which is connected to the
environment inside the initialize method, and I'm not sure whether this
could cause trouble by causing memory leaks in some circumstances.
Is there an easy way to simply bind a method object (in this case
#s.method(:s_method1) to a fixed argument?
My first idea was to use
#s.method(:s_method1).curry[choice]
but this does not achieve my goal. It would not return a callable Proc object, but instead actually execute s_method1 (this is not a bug, but documented behaviour).
Any other ideas of how my goal could be achieved?
Saving parameters separately
This option is simple, but it might not be what you're looking for :
class T
def initialize(s, choice=nil)
s = S.new(s)
#choice = choice
#pobj = s.method(choice ? :s_method1 : :s_method2)
end
def invoke
#pobj.call(*#choice)
end
end
T.new('no arguments').invoke
T.new('one argument', 12345).invoke
#=> s_method2 no arguments
#=> s_method1 12345 one argument
Method refinements for default parameters (Ruby 2.0+)
# Allows setting default parameters for methods, after they have been defined.
module BindParameters
refine Method do
def default_parameters=(params)
#default_params = params
end
def default_parameters
#default_params || []
end
alias_method :orig_call, :call
def call(*params)
merged_params = params + (default_parameters[params.size..-1] || [])
orig_call(*merged_params)
end
end
end
Here's an example :
def f(string)
puts "Hello #{string}"
end
def g(a, b)
puts "#{a} #{b}"
end
using BindParameters
f_method = method(:f)
f_method.default_parameters = %w(World)
f_method.call('user') # => Hello user
f_method.call # => Hello World
g_method = method(:g)
g_method.default_parameters = %w(Hello World)
g_method.call # => Hello World
g_method.call('Goodbye') # => Goodbye World
g_method.call('Goodbye', 'User') # => Goodbye User
Your code can be rewritten :
class T
using BindParameters
def initialize(s, *choice)
s = S.new(s)
#pobj = s.method(choice.empty? ? :s_method2 : :s_method1)
#pobj.default_parameters = choice
end
def invoke
#pobj.call
end
end
T.new('no arguments').invoke # => s_method2 no arguments
T.new('one argument', 12_345).invoke # => s_method1 12345 one argument
Monkey-Patching Method class (Ruby 1.9+)
If it is acceptable to patch the Method class, you could use :
class Method
def default_parameters=(params)
#default_params = params
end
def default_parameters
#default_params || []
end
alias_method :orig_call, :call
def call(*params)
merged_params = params + (default_parameters[params.size..-1] || [])
orig_call(*merged_params)
end
end
T becomes :
class T
def initialize(s, *choice)
s = S.new(s)
#pobj = s.method(choice.empty? ? :s_method2 : :s_method1)
#pobj.default_parameters = choice
end
def invoke
#pobj.call
end
end
Wrapping Method class (Ruby 1.9+)
This way is probably cleaner if you don't want to pollute Method class :
class MethodWithDefaultParameters
attr_accessor :default_parameters
attr_reader :method
def initialize(receiver, method_symbol)
#method = receiver.public_send(:method, method_symbol)
#default_parameters = []
end
def call(*params)
merged_params = params + (default_parameters[params.size..-1] || [])
method.call(*merged_params)
end
def method_missing(sym, *args)
method.send(sym, *args)
end
end
T becomes :
class T
def initialize(s, *choice)
s = S.new(s)
#pobj = MethodWithDefaultParameters.new(s, choice.empty? ? :s_method2 : :s_method1)
#pobj.default_parameters = choice
end
def invoke
#pobj.call
end
end
Any comment or suggestion are welcome!

How to call another classes methods from another class?

I have the following structure
class A
def method1
end
end
class B
#my = A.new
def classATest
#myT.method1
end
def newTest
classATest
end
end
class C
newB = B.new
newB.newTest
end
When I run class C, it gives me the error that it cannot find method1 of Class A (method newtest, calls method classATest, which calls the method1 using a global variable. What am I doing wrong? Is this not allowed?
Your line that says #my = A.new is not doing anything useful. It's making a new object and assigning it as an instance variable of class B, but that kind of variable cannot be used by instances of B without extra effort. You should replace that line with:
def initialize
#myT = A.new
end
Also, you had a typo: you wrote #my in one place and #myT in another.
Alternatively, keep the code the way you have it and replace #my and #myT with the name of a constant, such as MyA. Constants in Ruby start with capital letters, and can be used the way you are trying to use this variable.

Accessing the name of an anonymous class in superclass' self.inherited

I would like to access a class' name in its superclass MySuperclass' self.inherited method. It works fine for concrete classes as defined by class Foo < MySuperclass; end but it fails when using anonymous classes. I tend to avoid creating (class-)constants in tests; I would like it to work with anonymous classes.
Given the following code:
class MySuperclass
def self.inherited(subclass)
super
# work with subclass' name
end
end
klass = Class.new(MySuperclass) do
def self.name
'FooBar'
end
end
klass#name will still be nil when MySuperclass.inherited is called as that will be before Class.new yields to its block and defines its methods.
I understand a class gets its name when it's assigned to a constant, but is there a way to set Class#name "early" without creating a constant?
I prepared a more verbose code example with failing tests to illustrate what's expected.
Probably #yield has taken place after the ::inherited is called, I saw the similar behaviour with class definition. However, you can avoid it by using ::klass singleton method instead of ::inherited callback.
def self.klass
#klass ||= (self.name || self.to_s).gsub(/Builder\z/, '')
end
I am trying to understand the benefit of being able to refer to an anonymous class by a name you have assigned to it after it has been created. I thought I might be able to move the conversation along by providing some code that you could look at and then tell us what you'd like to do differently:
class MySuperclass
def self.inherited(subclass)
# Create a class method for the subclass
subclass.instance_eval do
def sub_class() puts "sub_class here" end
end
# Create an instance method for the subclass
subclass.class_eval do
def sub_instance() puts "sub_instance here" end
end
end
end
klass = Class.new(MySuperclass) do
def self.name=(name)
#name = Object.const_set(name, self)
end
def self.name
#name
end
end
klass.sub_class #=> "sub_class here"
klass.new.sub_instance #=> "sub_instance here"
klass.name = 'Fido' #=> "Fido"
kn = klass.name #=> Fido
kn.sub_class #=> "sub_class here"
kn.new.sub_instance #=> "sub_instance here"
klass.name = 'Woof' #=> "Woof"
kn = klass.name #=> Fido (cannot change)
There is no way in pure Ruby to set a class name without assigning it to a constant.
If you're using MRI and want to write yourself a very small C extension, it would look something like this:
VALUE
force_class_name (VALUE klass, VALUE symbol_name)
{
rb_name_class(klass, SYM2ID(symbol_name));
return klass;
}
void
Init_my_extension ()
{
rb_define_method(rb_cClass, "force_class_name", force_class_name, 1);
}
This is a very heavy approach to the problem. Even if it works it won't be guaranteed to work across various versions of ruby, since it relies on the non-API C function rb_name_class. I'm also not sure what the behavior will be once Ruby gets around to running its own class-naming hooks afterward.
The code snippet for your use case would look like this:
require 'my_extension'
class MySuperclass
def self.inherited(subclass)
super
subclass.force_class_name(:FooBar)
# work with subclass' name
end
end

Writing Ruby Libraries - hiding methods from outside the module

I'm writing a Ruby library which has a module with a bunch of classes inside it. Many of these classes need to be usable and modifiable by calling scripts, but I don't want (some of) the initializers to be visible/callable:
module MyLib
class Control
def initialize
# They can use this
end
def do_stuff
Helper.new('things')
end
end
class Helper
# Shouldn't be visible
def initialize(what)
#what = what
end
def shout
#what
end
end
end
c = MyLib::Control.new
h = c.do_stuff
p h.shout
# => "things"
# ^ All of this is desired
# v This is undesirable
p MyLib::Helper.new('!')
# => <MyLib::Helper #what='!'>
If it's a simple thing, then I'd also appreciate the generated RDoc not even include the .new method for the Helper class either. Any ideas?
Thanks for reading!
My original answer was completely wrong, as #Matthew pointed out. But there are other workarounds. For instance, you can assign an anonymous class to a class variable on Control, and still define methods as normal by using class_eval:
module MyLib
class Control
def initialize
end
def do_stuff
##helper.new('things')
end
##helper = Class.new
##helper.class_eval do
def initialize(what)
#what = what
end
def shout
#what
end
end
end
end
The snippet
c = MyLib::Control.new
h = c.do_stuff
p h.shout
still writes "things", but now there's no way to access ##helper except through the class variable. If someone really wants to access it my reopening the Control class or using class_eval, there's nothing to stop them, but that's just something you have to deal with in a dynamic language.
I chose to assign the anonymous class to a class variable so that it would only be created once; but if you don't care about redefining the anonymous class many times, there's no reason it couldn't be an instance variable.
Ruby has access control.

Resources