Ruby - extend built-in variables in Ruby - 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.

Related

In Ruby, how do I assign a value to a class instance

I want to create a class instance that has a value such that I can do something like puts a = Example.new(1) where a's value is specified in initialize.
I expect that this is a simple problem since all predefined Ruby classes allow this, but I'm unable to figure out how to do it for my classes.
Class#new and Return Values
Your example doesn't quite work because Ruby treats Class#new as a special case, and is expected to invoke the #initialize method and return an object. If it didn't, calling #new on a class would surprise a lot of people by returning the last evaluation of the initializer from your class, or from Object#new if it's otherwise undefined for your class. In either case, this would violate the principle of least surprise.
However, you can do what you want pretty easily by simply creating an accessor method and then chaining off of Example#new. For example, in Ruby 3.1.0:
class Example
attr_reader :int
def initialize(int) = (#int = int)
end
# prints `1` to STDOUT and assigns the value to *a*,
# but returns nil because you're using Kernel#puts
# which always returns nil
puts a = Example.new(1).int
# shows that the local variable *a* is set to the value
# returned by the Example#int accessor for the class'
# #int instance variable
a
#=> 1
To avoid the confusion of having a nil return value (even though this is expected with Kernel#puts, just change your puts statement to use Kernel#p instead:
p a = Example.new(2).int
#=> 2
Refactoring the Example Class for Older Rubies
If you're using an older Ruby than 3.0, you can't use an endless method or the improved handling for them in Ruby 3.1. The only difference is that rather than an inline method, you need to specify it with the standard def...end syntax, e.g.:
class Example
attr_reader :int
def initialize(int)
#int = int
end
end
Otherwise, the points above are valid as far back as any currently-supported Ruby version.

Define variable type Ruby in function declaration

How can i declare variable type in function definition?
In the next code:
def f(a, b)
#...
end
I can forget with what type it works with. How can i declare that it is a fixnum? Thanks
There are two points of confusion in your question:
Variables don't have types in Ruby. Only objects do.
Fixnum is not a type, it is a class. Classes aren't types. Protocols are types. Any object which speaks the correct protocol is of the same type. Some examples of well-known protocols in Ruby that do not have a corresponding class or mixin are what I will call the Appendable protocol (consisting of a method named << that appends its argument to its receiver) or the Iterable protocol (consisting of a method named each which yields each of its receiver's elements in turn). [Note: I made up those names. In reality, those Protocols are so deeply ingrained in Ruby culture that they don't even have names.]
In general, you will describe the protocol(s) your method expects from its arguments in the documentation of the method, but sometimes this description is left out if e.g. it is obvious from the name of the method or the name of the parameter.
See comments and the other answers for a clarification about what is really a "type".
How you can test a "variable's type" (in Ruby every variable references an object and we don't talk about types but classes)
How you can test for a class :
variable = MyClass.new
variable.class # => "MyClass"
variable.is_a?(MyClass) # => true
But typically in Ruby we are more interested in what the object can do, what we can do with them :
if variable.respond_to?(increment)
variable.increment
else
raise "Error : must provide as 'variable' an object that can increment itself"
end
Now to answer your question, I would "declare" the behavior in the function definition / comments using rdoc syntax
# Description of f
# Params:
# +a+:: command line string to be executed by the system
# +b+:: +Proc+ object that ....
def f(a, b)
# code
end

passing child class method as function argument in 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.

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.

Is it possible to compare private attributes in Ruby?

I'm thinking in:
class X
def new()
#a = 1
end
def m( other )
#a == other.#a
end
end
x = X.new()
y = X.new()
x.m( y )
But it doesn't works.
The error message is:
syntax error, unexpected tIVAR
How can I compare two private attributes from the same class then?
There have already been several good answers to your immediate problem, but I have noticed some other pieces of your code that warrant a comment. (Most of them trivial, though.)
Here's four trivial ones, all of them related to coding style:
Indentation: you are mixing 4 spaces for indentation and 5 spaces. It is generally better to stick to just one style of indentation, and in Ruby that is generally 2 spaces.
If a method doesn't take any parameters, it is customary to leave off the parantheses in the method definition.
Likewise, if you send a message without arguments, the parantheses are left off.
No whitespace after an opening paranthesis and before a closing one, except in blocks.
Anyway, that's just the small stuff. The big stuff is this:
def new
#a = 1
end
This does not do what you think it does! This defines an instance method called X#new and not a class method called X.new!
What you are calling here:
x = X.new
is a class method called new, which you have inherited from the Class class. So, you never call your new method, which means #a = 1 never gets executed, which means #a is always undefined, which means it will always evaluate to nil which means the #a of self and the #a of other will always be the same which means m will always be true!
What you probably want to do is provide a constructor, except Ruby doesn't have constructors. Ruby only uses factory methods.
The method you really wanted to override is the instance method initialize. Now you are probably asking yourself: "why do I have to override an instance method called initialize when I'm actually calling a class method called new?"
Well, object construction in Ruby works like this: object construction is split into two phases, allocation and initialization. Allocation is done by a public class method called allocate, which is defined as an instance method of class Class and is generally never overriden. It just allocates the memory space for the object and sets up a few pointers, however, the object is not really usable at this point.
That's where the initializer comes in: it is an instance method called initialize, which sets up the object's internal state and brings it into a consistent, fully defined state which can be used by other objects.
So, in order to fully create a new object, what you need to do is this:
x = X.allocate
x.initialize
[Note: Objective-C programmers may recognize this.]
However, because it is too easy to forget to call initialize and as a general rule an object should be fully valid after construction, there is a convenience factory method called Class#new, which does all that work for you and looks something like this:
class Class
def new(*args, &block)
obj = alloc
obj.initialize(*args, &block)
return obj
end
end
[Note: actually, initialize is private, so reflection has to be used to circumvent the access restrictions like this: obj.send(:initialize, *args, &block)]
Lastly, let me explain what's going wrong in your m method. (The others have already explained how to solve it.)
In Ruby, there is no way (note: in Ruby, "there is no way" actually translates to "there is always a way involving reflection") to access an instance variable from outside the instance. That's why it's called an instance variable after all, because it belongs to the instance. This is a legacy from Smalltalk: in Smalltalk there are no visibility restrictions, all methods are public. Thus, instance variables are the only way to do encapsulation in Smalltalk, and, after all, encapsulation is one of the pillars of OO. In Ruby, there are visibility restrictions (as we have seen above, for example), so it is not strictly necessary to hide instance variables for that reason. There is another reason, however: the Uniform Access Principle.
The UAP states that how to use a feature should be independent from how the feature is implemented. So, accessing a feature should always be the same, i.e. uniform. The reason for this is that the author of the feature is free to change how the feature works internally, without breaking the users of the feature. In other words, it's basic modularity.
This means for example that getting the size of a collection should always be the same, regardless of whether the size is stored in a variable, computed dynamically every time, lazily computed the first time and then stored in a variable, memoized or whatever. Sounds obvious, but e.g. Java gets this wrong:
obj.size # stored in a field
vs.
obj.getSize() # computed
Ruby takes the easy way out. In Ruby, there is only one way to use a feature: sending a message. Since there is only one way, access is trivially uniform.
So, to make a long story short: you simply can't access another instance's instance variable. you can only interact with that instance via message sending. Which means that the other object has to either provide you with a method (in this case at least of protected visibility) to access its instance variable, or you have to violate that object's encapsulation (and thus lose Uniform Access, increase coupling and risk future breakage) by using reflection (in this case instance_variable_get).
Here it is, in all its glory:
#!/usr/bin/env ruby
class X
def initialize(a=1)
#a = a
end
def m(other)
#a == other.a
end
protected
attr_reader :a
end
require 'test/unit'
class TestX < Test::Unit::TestCase
def test_that_m_evaluates_to_true_when_passed_two_empty_xs
x, y = X.new, X.new
assert x.m(y)
end
def test_that_m_evaluates_to_true_when_passed_two_xs_with_equal_attributes
assert X.new('foo').m(X.new('foo'))
end
end
Or alternatively:
class X
def m(other)
#a == other.instance_variable_get(:#a)
end
end
Which one of those two you chose is a matter of personly taste, I would say. The Set class in the standard library uses the reflection version, although it uses instance_eval instead:
class X
def m(other)
#a == other.instance_eval { #a }
end
end
(I have no idea why. Maybe instance_variable_get simply didn't exist when Set was written. Ruby is going to be 17 years old in February, some of the stuff in the stdlib is from the very early days.)
There are several methods
Getter:
class X
attr_reader :a
def m( other )
a == other.a
end
end
instance_eval:
class X
def m( other )
#a == other.instance_eval { #a }
end
end
instance_variable_get:
class X
def m( other )
#a == other.instance_variable_get :#a
end
end
I don't think ruby has a concept of "friend" or "protected" access, and even "private" is easily hacked around. Using a getter creates a read-only property, and instance_eval means you have to know the name of the instance variable, so the connotation is similar.
If you don't use the instance_eval option (as #jleedev posted), and choose to use a getter method, you can still keep it protected
If you want a protected method in Ruby, just do the following to create a getter that can only be read from objects of the same class:
class X
def new()
#a = 1
end
def m( other )
#a == other.a
end
protected
def a
#a
end
end
x = X.new()
y = X.new()
x.m( y ) # Returns true
x.a # Throws error
Not sure, but this might help:
Outside of the class, it's a little bit harder:
# Doesn't work:
irb -> a.#foo
SyntaxError: compile error
(irb):9: syntax error, unexpected tIVAR
from (irb):9
# But you can access it this way:
irb -> a.instance_variable_get(:#foo)
=> []
http://whynotwiki.com/Ruby_/_Variables_and_constants#Variable_scope.2Faccessibility

Resources