What's the purpose of an anonymous struct in Ruby? - ruby

They can be defined like this
Struct.new(:x, :y)
But what can usefully be done with them? Specifically, how can I create an instance of such a struct? This doesn't work
Struct.new(:x => 1, :y => 1)
(you get TypeError: can't convert Hash into String).
I'm using Ruby 1.9.2.
UPDATE:
Good pointers so far, thanks. I suppose the reason I asked this was that I have several times found myself wanting to do this
Struct.new(:x => 1, :y => 1)
just so that I can pass an object around where I can write obj.x instead of, say, instantiating a hash and having to write obj[:x]. In this case I want the structure to be really anonymous - I don't want to pollute my namespace with anything by naming what is returned from the Struct.new call. The closest thing to that, as already suggested is
Struct.new(:x, :y).new(1, 1)
But how do you like them apples? I'm not sure I do. Is it reasonable to expect to be able to define and instantiate an anonymous struct in one go (as part of core Ruby)? I guess when I read the official Ruby docs on Struct.new I assume the word 'anonymous' allows this, but it doesn't.

Struct.new returns a Class, so you can, for example, assign it to a constant like this:
Point = Struct.new(:x, :y)
or subclass it:
class Point < Struct.new(:x, :y)
# custom methods here
# ...
end
In both cases, you can use the resulting class like this:
Point.new(3, 5)
If you don't want to create a specific class (because you need to instantiate an object of that class only once), consider to use OpenStruct instead:
require 'ostruct'
point = OpenStruct.new(:x => 3, :y => 5)

You first create a struct, and then you can create instances of it. It's a way of creating data objects without having to declare a class. Basically it's the same as a hash, but it's more clean to access the objects. You can get stuff out of it by referencing it via ordinary accessor methods.
http://www.ruby-doc.org/core-1.9.3/Struct.html
# Create a structure with a name in Struct
Struct.new("Customer", :name, :address) #=> Struct::Customer
Struct::Customer.new("Dave", "123 Main") #=> #<struct Struct::Customer name="Dave", address="123 Main">
# Create a structure named by its constant
Customer = Struct.new(:name, :address) #=> Customer
Customer.new("Dave", "123 Main") #=> #<struct Customer name="Dave", address="123 Main">

Well, you can use Structs when you don't actually want to write a class with accessors. It's handy to just write
Project = Struct.new(:name)
instead of
class Project
attr_accesor :name
end
As tokland pointed out correctly (thanks!), a Struct also gives you a nice #initialize method automagically. So the following is possible without any further code:
Project = Struct.new(:name)
p = Project.new('Quadriloptic Curves')

I'm hot sure about purpose but Struct.new returns class so
irb(main):001:0> Struct.new(:x,:y)
=> #<Class:0x2914110>
irb(main):002:0> Struct.new(:x,:y).new(1,2)
=> #<struct x=1, y=2>

OpenStruct is probably what you want, but I ran into a situation recently where OpenStruct didn't work because I needed to raise an error when trying to access an undefined attribute. Struct does this:
os = OpenStruct.new
os.x = 1; os.y = 2;
os.z # returns nil
s = Struct.new(:x, :y).new
s.x = 1; s.y = 2;
s.z # raises NoMethodError
Just something to keep in mind.

As for creating instances:
User = Struct.new(:user,:password)
u = User.new("john","secret")

Related

Struct undefined local vars. Where to define?

Twitchuserspoints = Struct.new(:name, :points)
xuser = ""
unless ($pointsstructarray.include? xuser.name)
xuser = Twitchuserspoints.new(##username, 100)
$pointsstructarray << xuser.name
$pointsstructarray << xuser.points
else
$pointsstructarray[$pointsstructarray.index(xuser.points)+1] += 1
end
Where to define method 'name'? Also method '+' in else is undefined?
Struct.new(:name, :points) declares a struct itself. To instantiate it, one might either assign the result of above to the variable (or a constant as you do,) and then call Struct#new method on it.
Twitchuserspoints = Struct.new(:name, :points)
x = Twitchuserspoints.new(nil, 0) # arguments are optional
# or, if you don’t want to reuse it
# x = Struct.new(:name, :points).new
x.name = "seasonalz"
x
#⇒ #<struct Twitchuserspoints name="seasonalz", points=0>
Sidenote: using global and class variables is considered a bad practice and anti-pattern in most modern languages, including ruby.
Preliminaries
Without a lot more context, it's hard to see what you're really trying to do or why you're trying to define your behavior in a Struct. That doesn't mean it's wrong to do so; it's just not really obvious why you want to do it this way.
Inherit from Struct
That said, the code for inheriting from a Struct is pretty simple. In your case, you might be able to do something as simple as:
# Define a class that inherits from the Struct class.
class TwitchUser < Struct.new(:name, :points)
end
# Assign the struct to a variable.
twitcher = TwitchUser.new 'foo', 100
# Access the struct members.
twitcher.name #=> "foo"
twitcher.points #=> 100
You can then edit the values by assigning directly to a Struct member:
twicher.points = 200
Re-Open Class to Add Utility Methods
Alternatively, you can re-open your TwitchUser class to extend it with a method to do something with the members. For example:
class TwitchUser
def inc
self.points += 1
end
end
twitcher.inc #=> 101
twitcher.points #=> 101
All-in-One Approach
A very Ruby way to do this would be to declare the class to inherit from Struct, and include any utility methods you might want to operate on the members. For example:
class TwitchUser < Struct.new(:name, :points)
def inc
self.points += 1
end
end
Build Your Array of Struct Objects
Once you have the right type of object to represent your problem domain (whether or not it's a Struct), you can build an array of them. For example:
twitch_users = [twitcher]
twitch_users.first.name
#=> "foo"
twitch_users << TwitchUser.new('bar', 200)
twitch_users.last.name
#=> "bar"
You can then operate on the Array of Struct objects any way you like to find or display the records you want. As just one possible example:
twitch_users.select { |s| s.name.eql?('foo') }.first.points
#=> 101

How to create new binding and assign instance variables to it for availability in ERB?

I'm implementing HTML templating in a Ruby project (non-rails). To do this I'll be using ERB, but I have some concerns about the binding stuff.
First out, this is the method I got so far:
def self.template(template, data)
template = File.read("#{ENV.root}/app/templates/#{template}.html.erb")
template_binding = binding.clone
data.each do |k, v|
template_binding.local_variable_set(k, v)
end
ERB.new(template).result(template_binding)
end
To call it I'll just do
Email.template('email/hello', {
name: 'Bill',
age: 41
}
There are two issues with the current solution though.
First, I'm cloning the current binding. I want to create a new one. I tried Class.new.binding to create a new, but since binding is a private method it can't be obtained that way.
The reason I want a new one is that I want to avoid the risk of instance variables leaking into or out from the ERB file (cloning only takes care of the latter case).
Second, I want the variables passed to the ERB file to be exposed as instance variables. Here I tried with template_binding.instance_variable_set, passing the plain hash key k which complained that it wasn't a valid instance variable name and "##{k}", which did not complain but also didn't get available in the ERB code.
The reason I want to use instance variables is that it's a convention that the people relying on this code is familiar with.
I have checked some topics here at Stack Overflow such as Render an ERB template with values from a hash, but the answers provided does not address the problems I'm discussing.
So in short, like the title: How to create new binding and assign instance variables to it for availability in ERB?
1) No need to clone, new binding is created for you each time.
I have tested this in irb:
class A; def bind; binding; end; end
a = A.new
bind_1 = a.bind
bind_2 = a.bind
bind_1.local_variable_set(:x, 2)
=> 2
bind_1.local_variables
=> [:x]
bind_2.local_variables
=> []
2) Open the objects Eigenclass and add attr_accessor to it
class << template_binding # this opens Eigenclass for object template_binding
attr_accessor :x
end
So in ruby you can just open any class and add methods for it.
Eigenclass means class of a single object - each object can have custom class definition. Coming from C# I couldn't imagine a situation where this would be used, until now. :)
Do this for each hash
data.each do |k, v|
class << template_binding; attr_accessor k.to_sym; end
template_binding.k = v
end

Does Ruby have a method_missing equivalent for undefined instance variables?

When I invoke a method that doesn't exist, method_missing will tell me the name of the method. When I attempt to access a variable that hasn't been set, the value is simply nil.
I'm attempting to dynamically intercept access to nil instance variables and return a value based on the name of the variable being accessed. The closest equivalent would be PHP's __get. Is there any equivalent functionality in Ruby?
I do not believe this is possible in Ruby. The recommended way would be to use a ''user'' method rather than a ''#user'' instance var in your templates.
This is consistent with the way you deal with Ruby objects externally (''obj.user'' is a method which refers to ''#user'', but is actually not ''#user'' itself). If you need any kind of special logic with an attribute, your best bet is to use a method (or method_missing), regardless if you're accessing it from inside or outside the object.
See my answer to another similar question. But just because you can do it doesn't mean that it's a good idea. Sensible design can generally overcome the need for this kind of thing and allow you to produce more readable and hence maintainable code.
instance_variable_get seems to be the closest equivalent of PHP's __get from what I can see (although I'm not a PHP user).
Looking at the relevant Ruby source code, the only 'missing' method for variables is const_missing for constants, nothing for instance variables.
there isn't an instance_variable_missing (at least that I know of)
But why are you accessing randomly named instance variables anyway?
If your thread all the access to the object state through method calls (as you should anyway) then you wouldn't need this.
If you are looking for a way to define magic stuff without messing up with the method lookup, you may want to use const_missing.
A bit late but, instance_variable_missing is the same as method_missing to a point... Take the following class:
class Test
def method_missing(*args)
puts args.inspect
end
end
t = Test.new
Now let's get some instance variables:
t.pineapples #=> [:pineapples]
t.pineapples = 5 #=> [:pineapples=,5]
Not sure why the method is nil for you...
EDIT:
By the sounds of it you want to accomplish:
t = SomeClass.new
t.property.child = 1
So let's try returning a Test object from our previous example:
class Test
def method_missing(*args)
puts args.inspect
return Test.new
end
end
So what happens when we call:
t = Test.new
t.property.child = 1
#=>[:property]
#=>[:child=,1]
So this goes to show that this is indeed possible to do. OpenStruct uses this same technique to set instance variables dynamically. In the below example, I create EternalStruct which does exactly what you wanted:
require 'ostruct'
class EternalStruct < OpenStruct
def method_missing(*args)
ret = super(*args)
if !ret
newES = EternalStruct.new
self.__send__((args[0].to_s + "=").to_sym, newES)
return newES
end
end
end
Usage of EternalStruct:
t = EternalStruct.new
t.foo.bar.baz = "Store me!"
t.foo.bar.baz #=> "Store me!"
t.foo #=> #<EternalStruct bar=#<EternalStruct baz="Store me!">>
t.a = 1
t.a #=> 1
t.b #=> #<EternalStruct:...>
t.b = {}
t.b #=> {}
def t.c(arg)
puts arg
end
t.c("hi there") #=> "hi there"

Discovering Ruby object members?

What is an easy way to find out what methods/properties that a ruby object exposes?
As an example to get member information for a string,
in PowerShell, you can do
"" | get-member
In Python,
dir("")
Is there such an easy way to discover member information of a Ruby object?
"foo".methods
See:
http://ruby-doc.org/core/classes/Object.html
http://ruby-doc.org/core/classes/Class.html
http://ruby-doc.org/core/classes/Module.html
Ruby doesn't have properties. Every time you want to access an instance variable within another object, you have to use a method to access it.
Two ways to get an object's methods:
my_object.methods
MyObjectClass.instance_methods
One thing I do to prune the list of inherited methods from the Object base class:
my_object.methods - Object.instance_methods
To list an object's attributes:
object.attributes
There are two ways to accomplish this:
obj.class.instance_methods(false), where 'false' means that it won't include methods of the superclass, so for example having:
class Person
attr_accessor :name
def initialize(name)
#name = name
end
end
p1 = Person.new 'simon'
p1.class.instance_methods false # => [:name, :name=]
p1.send :name # => "simon"
the other one is with:
p1.instance_variables # => [:#name]
p1.instance_variable_get :#name # => "simon"
Use this:
my_object.instance_variables
object.methods
will return an array of methods in object

Adding an instance variable to a class in Ruby

How can I add an instance variable to a defined class at runtime, and later get and set its value from outside of the class?
I'm looking for a metaprogramming solution that allows me to modify the class instance at runtime instead of modifying the source code that originally defined the class. A few of the solutions explain how to declare instance variables in the class definitions, but that is not what I am asking about.
Ruby provides methods for this, instance_variable_get and instance_variable_set. (docs)
You can create and assign a new instance variables like this:
>> foo = Object.new
=> #<Object:0x2aaaaaacc400>
>> foo.instance_variable_set(:#bar, "baz")
=> "baz"
>> foo.inspect
=> #<Object:0x2aaaaaacc400 #bar=\"baz\">
You can use attribute accessors:
class Array
attr_accessor :var
end
Now you can access it via:
array = []
array.var = 123
puts array.var
Note that you can also use attr_reader or attr_writer to define just getters or setters or you can define them manually as such:
class Array
attr_reader :getter_only_method
attr_writer :setter_only_method
# Manual definitions equivalent to using attr_reader/writer/accessor
def var
#var
end
def var=(value)
#var = value
end
end
You can also use singleton methods if you just want it defined on a single instance:
array = []
def array.var
#var
end
def array.var=(value)
#var = value
end
array.var = 123
puts array.var
FYI, in response to the comment on this answer, the singleton method works fine, and the following is proof:
irb(main):001:0> class A
irb(main):002:1> attr_accessor :b
irb(main):003:1> end
=> nil
irb(main):004:0> a = A.new
=> #<A:0x7fbb4b0efe58>
irb(main):005:0> a.b = 1
=> 1
irb(main):006:0> a.b
=> 1
irb(main):007:0> def a.setit=(value)
irb(main):008:1> #b = value
irb(main):009:1> end
=> nil
irb(main):010:0> a.setit = 2
=> 2
irb(main):011:0> a.b
=> 2
irb(main):012:0>
As you can see, the singleton method setit will set the same field, #b, as the one defined using the attr_accessor... so a singleton method is a perfectly valid approach to this question.
#Readonly
If your usage of "class MyObject" is a usage of an open class, then please note you are redefining the initialize method.
In Ruby, there is no such thing as overloading... only overriding, or redefinition... in other words there can only be 1 instance of any given method, so if you redefine it, it is redefined... and the initialize method is no different (even though it is what the new method of Class objects use).
Thus, never redefine an existing method without aliasing it first... at least if you want access to the original definition. And redefining the initialize method of an unknown class may be quite risky.
At any rate, I think I have a much simpler solution for you, which uses the actual metaclass to define singleton methods:
m = MyObject.new
metaclass = class << m; self; end
metaclass.send :attr_accessor, :first, :second
m.first = "first"
m.second = "second"
puts m.first, m.second
You can use both the metaclass and open classes to get even trickier and do something like:
class MyObject
def metaclass
class << self
self
end
end
def define_attributes(hash)
hash.each_pair { |key, value|
metaclass.send :attr_accessor, key
send "#{key}=".to_sym, value
}
end
end
m = MyObject.new
m.define_attributes({ :first => "first", :second => "second" })
The above is basically exposing the metaclass via the "metaclass" method, then using it in define_attributes to dynamically define a bunch of attributes with attr_accessor, and then invoking the attribute setter afterwards with the associated value in the hash.
With Ruby you can get creative and do the same thing many different ways ;-)
FYI, in case you didn't know, using the metaclass as I have done means you are only acting on the given instance of the object. Thus, invoking define_attributes will only define those attributes for that particular instance.
Example:
m1 = MyObject.new
m2 = MyObject.new
m1.define_attributes({:a => 123, :b => 321})
m2.define_attributes({:c => "abc", :d => "zxy"})
puts m1.a, m1.b, m2.c, m2.d # this will work
m1.c = 5 # this will fail because c= is not defined on m1!
m2.a = 5 # this will fail because a= is not defined on m2!
Mike Stone's answer is already quite comprehensive, but I'd like to add a little detail.
You can modify your class at any moment, even after some instance have been created, and get the results you desire. You can try it out in your console:
s1 = 'string 1'
s2 = 'string 2'
class String
attr_accessor :my_var
end
s1.my_var = 'comment #1'
s2.my_var = 'comment 2'
puts s1.my_var, s2.my_var
The other solutions will work perfectly too, but here is an example using define_method, if you are hell bent on not using open classes... it will define the "var" variable for the array class... but note that it is EQUIVALENT to using an open class... the benefit is you can do it for an unknown class (so any object's class, rather than opening a specific class)... also define_method will work inside a method, whereas you cannot open a class within a method.
array = []
array.class.send(:define_method, :var) { #var }
array.class.send(:define_method, :var=) { |value| #var = value }
And here is an example of it's use... note that array2, a DIFFERENT array also has the methods, so if this is not what you want, you probably want singleton methods which I explained in another post.
irb(main):001:0> array = []
=> []
irb(main):002:0> array.class.send(:define_method, :var) { #var }
=> #<Proc:0x00007f289ccb62b0#(irb):2>
irb(main):003:0> array.class.send(:define_method, :var=) { |value| #var = value }
=> #<Proc:0x00007f289cc9fa88#(irb):3>
irb(main):004:0> array.var = 123
=> 123
irb(main):005:0> array.var
=> 123
irb(main):006:0> array2 = []
=> []
irb(main):007:0> array2.var = 321
=> 321
irb(main):008:0> array2.var
=> 321
irb(main):009:0> array.var
=> 123
Readonly, in response to your edit:
Edit: It looks like I need to clarify
that I'm looking for a metaprogramming
solution that allows me to modify the
class instance at runtime instead of
modifying the source code that
originally defined the class. A few of
the solutions explain how to declare
instance variables in the class
definitions, but that is not what I am
asking about. Sorry for the confusion.
I think you don't quite understand the concept of "open classes", which means you can open up a class at any time. For example:
class A
def hello
print "hello "
end
end
class A
def world
puts "world!"
end
end
a = A.new
a.hello
a.world
The above is perfectly valid Ruby code, and the 2 class definitions can be spread across multiple Ruby files. You could use the "define_method" method in the Module object to define a new method on a class instance, but it is equivalent to using open classes.
"Open classes" in Ruby means you can redefine ANY class at ANY point in time... which means add new methods, redefine existing methods, or whatever you want really. It sounds like the "open class" solution really is what you are looking for...
I wrote a gem for this some time ago. It's called "Flexible" and not available via rubygems, but was available via github until yesterday. I deleted it because it was useless for me.
You can do
class Foo
include Flexible
end
f = Foo.new
f.bar = 1
with it without getting any error. So you can set and get instance variables from an object on the fly.
If you are interessted... I could upload the source code to github again. It needs some modification to enable
f.bar?
#=> true
as method for asking the object if a instance variable "bar" is defined or not, but anything else is running.
Kind regards, musicmatze
It looks like all of the previous answers assume that you know what the name of the class that you want to tweak is when you are writing your code. Well, that isn't always true (at least, not for me). I might be iterating over a pile of classes that I want to bestow some variable on (say, to hold some metadata or something). In that case something like this will do the job,
# example classes that we want to tweak
class Foo;end
class Bar;end
klasses = [Foo, Bar]
# iterating over a collection of klasses
klasses.each do |klass|
# #class_eval gets it done
klass.class_eval do
attr_accessor :baz
end
end
# it works
f = Foo.new
f.baz # => nil
f.baz = 'it works' # => "it works"
b = Bar.new
b.baz # => nil
b.baz = 'it still works' # => "it still works"

Resources