Is there were a way to make an #property into a method with set and get, so, #property would call a method instead of returning an actual property, and #property = someval would also call a method instead of assigning to an actual property?
In my project, objects store values in a database. Consider this simple database module that stores records in memory. In my real life project it's a DBM like PostgreSQL:
module MyDB
RECORDS = {}
def self.create(pk)
RECORDS[pk] ||= {}
end
def self.set(pk, key, val)
return RECORDS[pk][key] = val
end
def self.get(pk, key)
return RECORDS[pk][key]
end
end
Objects have fields that are stored in that database. So, in this class, the species field is stored in and retrieved from the database:
class Pet
def initialize(pk)
#pk = pk
MyDB.create(#pk)
end
def species=(val)
MyDB.set #pk, 'breed', val
end
def species()
return MyDB.get(#pk, 'breed')
end
end
A simple use of the Pet class could look like this:
motley = Pet.new('motley')
motley.species = 'cat'
It works currently, but here's where I ran into an annoyance. I did something like this within the class:
def some_method(newval)
#species = newval
end
Then, when I ran the code I got this result:
motley.some_method 'whatever'
puts motley.species #=> cat
Then I realize that wasn't corrent and what I should have done is:
def some_method(newval)
self.species = newval
end
I think #species = newval makes sense. It feels like I'm setting a property of the object.
Is were a way to assign a method to the property, something like:
def :#species=(val)
return MyDB.set(#pk, 'breed', 'val')
end
def :#species
return MyDB.get(#pk, 'breed')
end
Is there a way to do such a thing? Should there be?
Is there a way to do such a thing?
No. In Ruby setter and getter methods are the way to get/set the internal state of an object. Instance variables are just lexical variables that are scoped to an instance.
Ruby is a language based on message passing and #foo = bar sends the message =, bar to the recipient that is the lexical variable #foo. If it called self##foo= instead that would break the entire model of the language.
Should there be?
Hell no.
Do we really need a completely new language feature just because you find it hard to remember to call self.foo= instead of #foo =? No.
Would this feature add anything to the language that cannot already be done? No.
Would it break existing code? Yes.
Related
I am trying to write a Greedy Algorithm for a certain problem. Simplified it looks like this:
There's an object called Foo with an randomized attribute called value and a method that changes this value change_value in a way that depends on an integer input
class Foo
def initialize
value = rand(1,10)
end
def change_value(input)
#changes the value in a certain way
end
end
Now the Greedy Algorithmus just gets the new value of Foo for all possible inputs and return the best input.
foo = Foo.new
best_value = 0
best_input = 0
(1..inputs).each do |k|
temp_foo = foo.clone
temp_foo.change_value(k)
if temp_foo.value>best_value
best_value = temp_foo.value
best_input = k
end
end
Foo.change_value(best_input)
The code works nearly as intended. The big problem is that the change_value-method within the each-funtion alters the temp_foo and the foo. What do I need to change to makes those objects completly dependent of each other? I also tried .dub by the way.
I think #clone or #dup won't work because they will share a reference to #value inside Foo.
In any case, you can do it more readably by changing Foo#change_value so it doesn't actually mutate the object but returns a copy:
class Foo
def initialize(value = nil)
#value = value || rand(10)
end
def change_value(input)
# returns a new Foo instance
Foo.new(#value + 1)
end
def value
#value
end
end
Because you're copying data in any case, using an immutable object (Value Object) is more general than some kind of deep clone.
I assume you assign value to the instance variable #value in Foo#initialize not the local variable value.
I also assume you don't have a simple primitive like in your code above but rather another object that contains a pointer, otherwise you most probably would not have such problem. In other words, I assume your change_value method makes an operation that relies on the #value pointer, such as #value[key] = some_new_value and not pure assignment, such as #value = some_new_object. When your object gets copied with clone or dup, that particular pointer is being copied, instead of the underlying structure, and therefore any calls to temp_foo.change_value will result in changes to foo's underlying #value.
To avoid this, you need to duplicate the object #value refers to. There is a trick you can use with Marshal, as discussed in this post, but I recommend against it since it causes a great deal of overhead. Instead, I would define a deep_dup method, such as below:
class Foo
def deep_dup
# Either this
#value = #value.dup
# OR this, and define the method #deep_dup in the class of #value
# to dup its internal structure too:
#value = #value.deep_dup
end
end
Then instead of doing temp_foo = foo.clone do temp_foo = foo.deep_dup.
I'm trying to implement a funky version of method chaining. Returning the instance of the class after each function call is easy, you just do
def chainable_method
some_code()
self
end
My idea is that the methods you can call depend on the previous method call. I'm trying to achieve this by returning an object belonging to the containing object. The contained object will have a few special methods, and then implement method_missing to return the containing object's instance.
Edit: The child object has some state associated with it that should be in itself, and not the parent. It might not have been clear previously as to why I need a whole instance for just method calls.
super is irrelevant in this case because the contained object doesn't inherit from the containing object, and I wouldn't want to call the containing object's methods on the contained object anyway - I want to call the containing object's methods on the containing object itself. I want the containing object, not the containing object class.
Not sure if this is possible.
Edit: reworded everything to use "containing/contained object" instead of the completely incorrect parent/child object.
Also, I'm using 1.9.3, if that matters. Version isn't important, I can change if needed.
My explanation was probably unclear. Here's the code:
class AliasableString
def initialize(string)
#string = string
end
def as(aka)
#aka = aka
end
def has_aka?
!#aka.nil?
end
# alias is a reserved word
def aka
#aka
end
def to_s
#string + (self.has_aka? ? (" as " + #aka) : "")
end
end
class Query
def initialize
#select_statements = Array.new
end
def select(statement)
select_statement = AliasableString.new(statement)
#select_statements.push(select_statement)
select_statement
end
def print
if #select_statements.size != 0
puts "select"
#select_statements.each_with_index {| select, i|
puts select
}
end
end
end
# Example usage
q0 = Query.new
q0.select("This is a select statement")
.select("Here's another one")
.as("But this one has an alias")
.select("This should be passed on to the parent!")
q0.print
I haven't yet fully implemented print. AliasableString needs to have #string and #aka separate so I can pull them apart later.
First of all, it doesn't matter what class of object is contained within a Query instance. All of the syntax shown on your 'example usage' section is appropriately defined in Query. The only requirement of the objects contained within a query instance is that they respond to as (or some similar method). What you have here is something like a state machine, but the only state that really matters is that some object occupies the last position in the select_statements array. Here's how I would build this (again, based mostly on your example at the end, I'm afraid I can't quite follow your initial explanation):
class Query
# ... initialize, etc.
def select(statement, statement_class = AliasableString)
select_statements << statement_class.new(statement)
self
end
def as(aka)
# this will only ever be used on the most recent statement added
statement_to_alias = select_statements.last
# throw an error if select_statements is empty (i.e., :last returns nil)
raise 'You must add a statement first' unless statement_to_alias
# forward the message on to the statement
statement_to_alias.as(aka)
# return the query object again to permit further chaining
self
end
end
AliasableString doesn't need to know a thing about Query; all it needs to do is respond appropriately to as.
- EDIT, SOLVED -
Ended up creating a method Object#rec to accomplish what I needed, this is the result:
#$l stores the last object on which Object#rec was called
$l=nil;class Object;def rec;$l=self;end;end
class String
attr_accessor :ref
alias_method :old_reverse, :reverse
def reverse
self.rec.old_reverse
end
def my_method
$l.ref + ' ' + self
end
end
a = "Hello"
b = "dlroW" ; b.ref = a
p b.reverse.my_method #=> Hello World
If anyone has a better way the question is still open.
- EDIT, SOLVED -
The problem:
I have a situation similar to this:
obj.method1.method2
where method1 returns something other than obj and I need method2 to access obj again as it holds a reference I need.
For example:
class String
attr_accessor :ref
def my_method(b)
b.ref + ' ' + self
end
end
a = "Hello"
b = "dlroW" ; b.ref = a
#I want my_method to access 'b.ref' without having to pass 'b'
p b.reverse.my_method(b) #=> Hello World
Alternative:
I know I could avoid having to pass b again if I used obj.my_method and my_method did both reversing(for the example) and accessing the reference, or like commented by the Tin Man have method1 change obj but return the original obj, but what I want is to know if it's possible or not to accomplish the above.
Thanks in advance.
Sounds kind of like you're looking for Object.tap:
Yields x to the block, and then returns x. The primary purpose of this method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain.
For your example, you might be able to use String's reverse! inside the tap to manipulate the object. For your application, manipulate the object as you desire inside tap, then the object will be passed on to your following method.
I am writing a class in Ruby where I have instance variables (i.e. #person_summary_info, #name, #dob, #favorite_food) for the class.
To parse a piece of text, I have a public method that I call from outside the class (let's call it interpret).
This method calls some private class methods such as get_name that use #person_summary_info to extract the respective piece of information (in this case, the name of the person). Should those private methods:
a) use the instance #person_summary_info, or get that information through a parameter passed to them (i.e. get_name vs get_name(person_summary_info))
b) modify the instance variable directly and return nothing, or modify nothing outside the scope of the function, and return the result (i.e. inside get_name, set #name = 'John', or return 'John')?
What is the best practice here?
Thanks!
I have included my best representation of your question in code at the bottom of my answer, but I'd like to present my solution as I understand your dilemma first...
Do this if your name attribute is meant to be publicly accessible:
class Person
attr_accessor :name
def initialize(name)
#name = name
end
def interpret(text_to_parse)
# I have no idea what you are parsing in real life
self.name = text_to_parse.split.last
end
end
person = Person.new("Frederick")
puts person.name
# => "Frederick"
person.interpret("Please, call me Fred")
puts person.name
# => "Fred"
Do this if your name attribute should not be (easily) publicly accessible: (For what it's worth, pretty much anything can be accessed one way or another in Ruby. One of the many things that make it awesome!)
class Person
def initialize(name)
#name = name
end
def interpret(text_to_parse)
# I have no idea what you are parsing in real life
#name = text_to_parse.split.last
end
end
person = Person.new("Frederick")
puts person.instance_variable_get("#name")
# => "Frederick"
person.interpret("Please, call me Fred")
puts person.instance_variable_get("#name")
# => "Fred"
And, as mentioned above, here's my best translation of your question into code:
class Person
def initialize
#person_summary_info = { name: "foo" }
#name = "bar"
#dob = "baz"
#favorite_food = "beer"
end
def interpret(text_to_parse)
# Some kind of parsing?
get_name_1
# OR
get_name_2(#person_summary_info)
# OR
get_name_3
# OR
#name = get_name_4
end
private
def get_name_1
#person_summary_info[:name]
end
def get_name_2(person_summary_info)
person_summary_info[:name]
end
def get_name_3
#name = 'John'
end
def get_name_4
'John'
end
end
Hopefully, you can see why there's some confusion in the comments about what you are asking exactly. If nothing else, maybe seeing this will help you to form your question more clearly so we can help!
Finally, you should avoid writing your own getters/setters in Ruby unless you need to hook in some custom code to the getting/setting processes -- use the class-level attr_reader/attr_writer/attr_accessor macros to create them for you.
If interpret() is not meant to change the state of a particular instance of Person, then consider naming the method something like get_name_from_string(string) and possibly making it static, since it doesnt do anything to the state of the instance.
If you want interpret() to change the state of a particular instance of Person, then consider changing the name of the method, prefixing it with set and include the attribute name being set (set_name_from_string()). If several attributes are being set, then perhaps set_from_string() and include a code comment stating what instance variables are being modified. Internally the method could call get/set_name() as described below.
Typically, getter/setter methods are public and should be quite simple, doing what their name suggests:
- getName() returns the instance variable #name
- setName(name) sets or overwrites the instance variable #name with the value passed in and returns nothing
In Java, this is a type of POJO, specifically Java Beans (excluding the part about needing to be serializable) Its very common programming practice in several different languages to have public setter/getter methods for the instance variables and to also have a default constructor (one that takes no arguments) and another constructor allowing you to set the instance variables upon instantiation of the Object.
using #instance directly from another class is a good way how to get into troubles. Each class should have it's own variables and anything you would like to process or return back should be assigned/returned directly.. that means that way
#instance = my_class.get_name(person_summary_info)
and not
my_class.get_name
Just try to imagine how to test that code using #instance variables and chance to reuse that piece of code..
just my 2c
I have a class, Foo. I want to be able to pass the constructor a Foo instance, foo and get the same instance back out.
In other words, I want this test to pass:
class Foo; end
foo = Foo.new
bar = Foo.new(foo)
assert_equal foo, bar
Anyone know how I can do that? I tried this:
class Foo
def initialize(arg = nil)
return arg if arg
end
end
foo = Foo.new
bar = Foo.new(foo)
assert_equal foo, bar # => fails
but it doesn't work.
Help?
EDIT
Because a number of people have asked for my rationale:
I'm doing rapid analysis of lots of data (many TB) and I am going to have a lot of instances of a lot of objects. For some of these objects, it doesn't make sense to have two different instances with the same data. For example, one such object is a "window" (as in temporal window) object that has two properties: start time and end time. I want to be able to use the constructor in any of these ways and get a window object back:
window = Window.new(time_a, time_b)
window = Window.new([time_a, time_b])
window = Window.new(seconds_since_epoch_a, seconds_since_epoch_b)
window = Window.new(window_obj)
window = Window.new(end => time_b, start => time_a)
...
Some other object that needs a window might be instantiated this way:
obj = SomeObj.new(data => my_data, window => window_arg)
I don't necessarily know what's in window_arg, and I don't really care -- it will accept any single argument that can be interpreted by the Window constructor. In the case of already having a Window instance, I'd rather just use that instance. But the job of interpreting that seems like a concern of the Window constructor. Anyway, as I mentioned I'm churning through many TB of data and creating lots of instances of things. If a window object gets passed around, I want it just to be recognized as a window object and used.
By definition, constructors are meant to return a newly created object of the class they are a member of, so, no you should not override this behavior.
Besides, in Ruby, new calls initialize somewhere within its method body, and its return value is ignored, so either way the value you return from initialize will not be returned from new.
With that said, I think that in your case, you might want to create a factory method that will return different Foo objects based on arguments passed to the factory method:
class Foo
def self.factory(arg = nil)
return arg if arg.kind_of? Foo
Foo.new
end
end
foo = Foo.factory
bar = Foo.factory(foo)
assert_equal foo, bar #passes
def Foo.new(arg=nil)
arg || super
end
initialize is called by new which ignores its return value. Basically the default new method looks like this (except that it's implemented in C, not in ruby):
class Class
def new(*args, &blk)
o = allocate
o.send(:initialize, *args, &blk)
o
end
end
So the newly allocated object is returned either way, no matter what you do in initialize. The only way to change that is overriding the new method, for example like this:
class Foo
def self.new(arg=nil)
if arg
return arg
else
super
end
end
end
However I'd strongly advise against this since it runs counter to many expectations that people have when calling new:
People expect new to return a new object. I mean it's even called new. If you want a method that does not always create a new object, you should probably call it something else.
At the very least people expect Foo.new to return a Foo object. Your code will return whatever the argument is. I.e. Foo.new(42) would return 42, an Integer, not a Foo object. So if you're going to do this, you should at the very least only return the given object, if it is a Foo object.
Does not work for:
class Some
def self.new( str )
SomeMore.new( str )
end
end
# the Some is parent of SomeMore
class SomeMore < Some
def initialize( str )
#str = str
end
end
For this particular use case, it might be better to use one of these approaches.
class Foo
def self.new(args=nil)
##obj ||= super(args)
end
end
class Foo
def self.new(args)
##obj = super(args)
end
def self.new
##obj
end
end
This allows you to have only a single object that gets created that can be used universally, but returns an object of the Foo class, making it fall more inline with standard expectations of a new method, as Jacob pointed out.