passing child class method as function argument in ruby - ruby

There is a class Commerce and its constructor will initialize an object named commerce. LineItem is a class with in the Commerce class. LineItem class has a method named "ssm". when this method "ssm" is invoked it will initialize an object of another class named SSM. In our problem, we need pass to the "ssm" method as a function argument in another Test class.
def autoUpdate(docNum, type, varName, value)
type = method(Commerce::LineItem:ssm)
commerce.line_item.type(varName).set(value)
end
In the place of the function parameter 'type', it should be replaced by different method names from the class LineItem. However the above function autoUpdate throws an error. How to pass child class methods as function parameters in another class.

I think what you're doing is probably a mistake and a terrible design, but for your specific question if you actually want a method object, I think you want:
type_method = Commerce::LineItem.instance_method(:ssm)
You could then call it by:
bound_type_method = comerce.line_item.bind(type_method).call(varName).set(value)
But this doesn't make a lot of sense. You could also just pass the symbol, method_name = :ssm, and later call commerce.line_item.type.send(method_name, varName).set(value)
That's still kind of a mess though, this is scary code.
Also there appear to be 'child classes' involved here.

Passing methods as arguments and changing their receivers is not as easy to do in Ruby as it is in, say, Javascript, nor is it as conventional. Instead, usually when this sort of thing needs to be done, the Ruby convention is to use blocks. E.g. if you defined your auto_update method like this
def auto_update(doc_num, var_name, value)
raise ArgumentError, "Block is required." unless block_given?
line_item = yield var_name
line_item.set(value)
end
Then you might call it like this
auto_update(doc_num, var_name, value) do |var_name|
commerce.line_item.ssm(var_name)
end
Granted that looks pretty silly, for a number of reasons (for instance, why not just call the contents of the block first and then pass in the result as an argument for auto_update). If you describe your design a bit more, in particular from where this auto_update method is being called and how the value for type is determined, that might help.

Related

Rails and ruby class, instance variables

I'm new in ruby on rails and have a little confusion on the following code
module Comment
class CommentScore < ActiveRecord::Base
self.table_name = 'comment_scores'
# Associations
belongs_to :provider_account
# Scopes
scope :by_provider_account_id, lambda { |provider_account_id| where(provider_account_id: provider_account_id) }
# Instance methods
def set
return unless self.valid?
return if unsettable?
self.positive_count = provider_account.comment.total(:positive)
self.total = provider_account.comment.total(:all)
self.score = decimal
self.save!
end
def decimal
positive_count.to_d / total.to_d
end
end
end
from my studies, I learned that if the 'self' keyword is used inside a method, it will just actually call a method like in this example, self.positive_count, calls the method 'positive_count' which in rails, means the table column named 'positive_count'.
So I got a bit confused in the 'decimal' method, it just use 'positive_count' without the self? Isn't it a local variable?
ActiveRecord will define getters and setters for each field in your model, eg. in this case you will get positive_count and positive_count= defined.
You can call the getter easily, like you are doing in your decimal method. However, to call the setter like you are in your set method, you need to use self - otherwise you will simply set a local variable in that method with the value, instead of calling the setter.
When there is such local variable as positive_count, then the form positive_count without the period and arguments will be interpreted as a local variable. If not, then it will be interpreted as a method. However, methods of the form foo= are obligatorily private.
Based on context and what methods have been defined, Ruby will try and determine if you're making a method call or reading from or assigning to a variable. Unlike other languages where local variables must be defined in advance (int total), or where variables are specified with a different syntax ($int), Ruby has a syntax that's a lot more stripped down. This can lead to some ambiguity.
If there's a method defined with a particular name, like decimal or there's a method that accepts values, such as total=, then a method call will be made when these are referenced. Otherwise it's presumed they're variables.
Use of self avoids ambiguity and forces a method call. It's often used to be sure there's no risk of creating a variable by accident.
Consider the following:
def example
total = 50
end
Is total a variable, or is there a method called total= that can be used instead? It's not clear from this short example. By default Ruby will treat that as a variable.
Now if you have a column in the database called total, this is discovered after the original model code has been loaded. At that time there was no method called total=. This method is created dynamically after the first instance of that model is instantiated. This is where you get the self-dot notation showing up:
def example
self.total = 50
end
This ensures you're assigning to the attribute and not creating a local variable by mistake.

Accessing functions inside a Ruby block

I'm working inside a one-off Ruby script (so not inside an explicitly defined module or class) and I'm having a hard time accessing a function I've defined earlier in the script, from within a .each block.
def is_post?(hash)
if hash["data"]["post"] == "true" #yes, actually a string
true
else
false
end
end
#further down
threads["data"]["children"].each do |item|
puts item["data"]["title"] unless item.is_post?
end
Result:
in 'block in <top (required)>': private method `is_post?' called for #<Hash:0x007f9388008cf0\> (NoMethodError)
threads is a very, very nested hash. A hash, contaning a hash of arrays, the arrays contain a hash with header data, which contains another hash with the rest of the details. A bit messy, but I didn't write the module that generates that :P
The idea is to iterate through the arrays and retrieve the data from each one.
My questions are:
What manner of shenaniganery do I need to do to access my is_post? function from within the block?
Why is it coming up as a private method when I don't have any private declarations anywhere in my script?
Kernel vs instance method, self vs argument
def is_post?(hash)
...
end
By defining the methods in that way, you are defining a method for Kernel. You have the choice of either calling this method through Kernel.is_post?(hash), or is_post?(arg). Unless item is the Kernel object, you wont have defined the method is_post? for it.
Your method takes exactly one argument. In case item has a is_post? method, by doing item.is_post?, you are not providing an argument but only self to the method.
The solution
You probably should replace
item.is_post?
by
is_post?(item)
You don't want to call is_post? on the item (it's a Hash like the error message says).
What you want is the following:
threads["data"]["children"].each do |item|
puts item["data"]["title"] unless is_post?(item)
end

Ruby - extend built-in variables in Ruby

I would like to extend the functionality of variables in Ruby. The reason is I am working on something similar of a type system or value checker (this sounds a bit crazy but the whole idea is to long to explain, just the reason I would like to extend default variables).
I have read that in Ruby, everything is an object; so variables are objects. And Ruby is supposed to be quite liberal concerning what can be changed via meta-programming.
Is there some kind of 'Class' associated with local variables that I could extend?
I would like to associate a string-variable for each variable that holds a string representation of a type. Further I would like to intercept variable assignments and execute a method each time a new value is assigned to a variable. This is so I can check, if the new value is correct according to the type (stored as string in the Variable).
If local variables in Ruby are defined as object of a class, I can extend that class or modify it via a ruby mixin.
The workaround would be to create a new class for my Variables (and not use the build in local variables of Ruby). This class can have a value attribute, a attribute (stored as string) for the type and a get- and set-method. This way I can solve my problem, but I would like to extend the built in variables in ruby, if that is possible.
current work in progress
class Fixnum
attr_accessor :tp
#tp
def mytype ( type )
#tp = type
end
def typecheck
#call typechecker
puts "checked"
end
end
Test Code:
a = 3
a.mytype("nat")
puts a.tp
a.typecheck
There are still two problems.
First, I think it is not possible to add a new constructor to Fixnum. Second, I would like to intercept the variable access, i.e. "b = a" calls the method 'typecheck' of a. But this would require something similar to Aspect Oriented Programming and I am not sure if this can be solved with Ruby's meta-programming facilitates.
I have read that in Ruby, everything is an object
That depends on your definition of "object" and every-"thing". "Object" can mean "entity that can be manipulated by the program" (which I will call object from now on), or "value that is a member of the object system" (which I will call Object from now on).
In Ruby, everything that can be manipulated by the program (i.e. every object) is also an Object, i.e. an instance of a class. This is unlike Java, for example, where primitives can be manipulated by the program (i.e. are objects in that sense of the word), but aren't Objects. In Ruby, this distinction doesn't exist: every object is an Object and every Object is also an object.
However, there are things in the language, which cannot be manipulated by the program and which aren't instances of a class, i.e. they are neither object s nor Objects. These are, for example, methods, variables, syntax, parameter lists, arguments lists, keywords.
Note: you can use Ruby's reflection API to give you an object that represents a method or a parameter list, but that object is only a proxy, it is not the real thing.
So, when we say "everything is an object", what we really mean is that "every object is an Object", i.e. that everything which can be manipulated by the program is also a member of the object system, or in other words, there are no values outside of the object system (unlike primitives in Java). We do not mean that everything that exists in the language can also be manipulated at runtime by the program.
so variables are objects
No, unfortunately, they are neither object s nor Objects.
This is also clearly stated in the Ruby Language Specification (emphasis added by me):
6.2 Variables
6.2.1 General description
A variable is denoted by a name, and refers to an object, which is called the value of the variable.
A variable itself is not an object.
In the book The Ruby Programming Language by Matz and David Flanagan it says on page 2:
every value is an object
Note, it doesn't say every-thing, only every value.
See also the question Is variable is object in ruby?
There are a couple things you can do. For starters, all (or very nearly all) Ruby classes (including "primitives" such as numbers) support a to_s method which returns a string representation of the object. For numbers, to_s will just return the string representation of that number (e.g., "42" for 42). The string value returned for other classes will vary. The nice thing is that you can override a class's methods via "monkey patching". Here's an extremely contrived example:
class Array
def to_s
return "An array of size #{self.size}."
end
end
a = [1, 2, 3, 4]
puts a.to_s
# => "An array of size 4."
For your other question regarding executing a method every time a variable's value is set, the way to handle this is to always interact with the variable through its accessor methods. This way you can implement custom code inside a property's getter and setter methods (or simply call another method from inside the accessor). Like this:
class MyClass
# Use the default getter for var.
attr_reader :var
def initialize
#var = 1
end
# Custom setter for var.
def var= v
#var = v
puts "var changed to #{v}"
end
end
mc = MyClass.new
mc.var = 9
# => "var chaged to 9"
You could do something like this, but it only works for globals:
$type_checked = {:$a => String, :$b => Array}
$type_checked.keys.each do |var|
trace_var(var) do |obj|
puts "hey, don't assign #{var} to a #{obj.class}" unless $type_checked[var] == obj.class
#or raise an Error
end
end
$a = 1
$a = "a"
$b = 1
#output:
#hey, don't assign $a to a Fixnum
#hey, don't assign $b to a Fixnum
But this clearly goes against the grain of the language.

Trying to learn / understand Ruby setter and getter methods

I'm just learning to program and have decided to try Ruby. I'm sure this is a stupid question, but the instructor is talking about setter and getter methods, and I'm confused. Here is the example:
class Human
def noise=(noise)
#noise = noise
end
def noise
#noise
end
end
From this, the class is instantiated, and I can puts this out:
man = Human.new
man.noise=("Howdie!")
puts man.noise
This results in Howdie!
Now what confuses me is that the instructor is saying without the getter method (the 2nd of the two methods), there is no way to interact with the instance variable #noise.
But when I remove the getter method, I'm able to still access #noise, see example:
class Human
def noise=(noise)
#noise = noise
end
end
man = Human.new
puts man.noise=("Howdie!")
This works the same as when the getter method it used.
So now I'm confused. Why is the getter needed? What does the instructor mean by not being able to access the instance variable without it? Is it possible he's using an older version of Ruby?
Thanks in advance for your help.
You can interact with that instance variable from other methods belonging to that instance, even if there is no getter:
def noise=(noise)
#noise = noise
end
def last_noise
#noise
end
There doesn't need to be a getter defined with the same name as the method; the two are not linked at all. The getter is needed to "get" the value of the instance variable, but only in a short syntax.
What's happening in your example is that you're initializing a new object (Human.new), and then using a method (noise=, yes the method name contains the = symbol) that just-so-happens to define an instance variable (that is, a variable just for that instance), and then finally retrieving that instance variable with another method call.
You can actually use instance_variable_get to get the instance variable without defining any getter at all:
man = Human.new
man.noise = "Howdie"
man.instance_variable_get("#noise")
This will return "Howdie", even though there is no getter defined.
And no, I don't think he's using an older version of Ruby.
The line of code
puts man.noise=("Howdie!")
does NOT use the getter method, so the getter method does not need to be defined for it to work. That line just uses the setter method. The return value of the setter method is automatically equal to whatever is on the right-hand side of the equal sign, so "Howdie!" gets passed to puts.
The line of code
puts man.noise
DOES use the getter method, and it would not work if you remove the getter method.
Surely they both return a value, but their behavior are different.
Let's say there is already a member #a .
with getter, one obtains the current value of #a, without modifying it.
with setter, one modifies #a, and get its new value as return value.
when thinking about behavior of setter, note:
the old value of #a can not be obtained with setter, and got overwritten.
what returned by setter, is actually already known before invoking setter.
May be the attention for getters and setters is because some other languages allow you to access class variables directly. Python:
class k:
i = 100
print k.i # 100
k.i = 200
print k.i # 200
In contrast, Ruby keeps all of its variables completely private to the class and only exposes them through accessor methods.
In your example, if you remove the getter, you can indeed interact with the variable (that is: change it) trough the setter, but you can't get it back (in a regular way) when you need it.

Attach method to another class in restricted context in ruby

Is it possible to attach my own methods to another class, in limited region ?
If so, could anyone show me a better practice, or is it supposed to use something like deligate to do this?
The situation is like this : In class A that receive, generate and pass out instance of class B,
I want to attach some method onto those bs, without leaving those new methods accessible outside class A.
These are called singleton methods. You can add methods to any object, only affecting that instance, not the entire class it was created from.
some_object = Whatever.new
other_object = Whatever.new
class << some_object
def another_method
...
end
end
some_object.another_method # works
other_object.another_method # error: no such method another_method
You can also use define_singleton_method:
some_object.define_singleton_method(:foo) do |arg1, arg2|
puts "Called with #{arg1} and #{arg2}"
end
some_object.foo(7, 8)
There's also instance_eval, but you get the idea ;)
You can write a private method in class B that takes an A object as an argument and uses instance_variable_get, instance_variable_set, and send to access whatever data from the object you want. This is pretty ugly though.

Resources