Creating a new variable automatically - Generating a variable - Ruby - ruby

Meta info: Found similar posts, zero were for Ruby.
Created title using multiple terms for search accessibility
Okay now onto the question...
I'm trying to generate new variables (moreso) automatically, I wanted to create a mechanism that did so.
class User
attr_accessor :name
def initialize(name)
#name = name
end
end
def CreateUser(name)
name = User.new(name)
end
CreateUser("Arnold")
p Arnold
I used a single argument in the name parameter of method "CreateUser" as both the name of the variable that I want to declare and the information sent into the code creating a member of my class.
I quickly remembered that in Ruby, variables inside of a method are local to only the method. If that weren't the case, and I could spit out the new variable from the method then I could generate my users easily - but this isn't the case. Is there a work around for this? Or is declaring a new variable manually just the name of the game?

Your question is a little confusing. At first glance, it seems like you're looking for some metaprogramming technique to dynamically create instance variables, such as:
class User
def new_variable name, value
instance_variable_set "##{name}", value
end
end
However, upon looking more closely at the problem you're actually trying to solve, it seems like the real issue is that you aren't making your methods part of your original object. This is probably what you really want:
class User
attr_accessor :name
def initialize name
#name = name
end
def create_user
# do whatever you need to do to "create" a user
pp name
end
end
u = User.new 'Arnold'
u.create_user
Enabling an object to call instance methods on data stored within the object is part of encapsulation. That is most likely what you're really after.

Related

When are 'attr_writer' used without 'attr_reader?

There is a question where #coreyward talks about using it alone, but doesn't give an example of when. Other sources on the web allude to some use cases, but I have yet to find any source that lists a real world example. When would you want to write data but not be able to read it?
It isn't always, necessarily, that you don't want to read it; sometimes the default writer is fine, but you want a custom reader. Take, for instance, this person class:
class Person
attr_writer :name
attr_accessor :title
def initialize(name, title)
#name = name
#title = title
end
def name
"#{#title} #{#name}"
end
end
Here, creating a reader through attr_reader or attr_accessor for name would be redundant, since I'm defining my own name method because there is a custom rule, that I should always display the name with a title.
As an aside, one could argue that my name method should be something more along the lines of name_with_title or what have you, but I still wouldn't actually want the default name method from a reader, because I still want all access to the name to go through this other method.

Understanding the access of a variable assigned in initialize in Ruby

As a beginner, I've not quite got my head around self so I'm having trouble understanding how the self.blogs in initialize, and blogs then self.blogs on the next line after in the add_blog method, are all working together in the below code.
Why does blogs in the add_blog method access the same variable as self.blogs in initalize?
And then why is self.blogs used afterwards to sort the blogs array?
Also, would it matter if I used #blogs in initialize, instead of self.blogs?
class User
attr_accessor :username, :blogs
def initialize(username)
self.username = username
self.blogs = []
end
def add_blog(date, text)
added_blog = Blog.new(date, self, text)
blogs << added_blog
self.blogs = blogs.sort_by { |blog| blog.date }.reverse
added_blog
end
end
To answer your question, we have to reveal the true nature of attr_accessor.
class Foo
attr_accessor :bar
end
is completely equivalent to
class Foo
def bar
#bar
end
def bar=(value)
#bar = value
end
end
You can see that attr_accessor :bar defines two instance methods Foo#bar and Foo#bar= that access an instance variable #bar.
Lets then look at your code.
self.blogs = [] in initialize is actually calling the method User#blogs=, and through it sets the instance variable #blogs with an empty array. It can be written as self.blogs=([]) but it's noisy, isn't it? By the way, you can't omit self. here otherwise it just sets a local variable.
blogs << added_blog calls the method User#blog which returns the value of #blogs. It can also be written as self.blogs().push(added_blog), but again it's not rubyish. You can omit self. because there is no local variable named blogs in User#add_blog, so ruby falls back to call the instance method.
self.blogs = blogs.sort_by { |blog| blog.date }.reverse mixes call to User#blogs= and User#blogs.
For most method calls on self, self.method_name is equivalent to just method_name. That's not the case for methods whose name ends with an =, though.
The first thing to note, then, is that self.blogs = etc doesn't call a method named blogs and then somehow 'assign etc to it'; that line calls the method blogs=, and passes etc to it as an argument.
The reason you can't shorten that to just blogs = etc, like you can with other method calls, is because blogs = etc is indistinguishable from creating a new local variable named blogs.
When, on the previous line, you see a bare blogs, that is also a method call, and could just as easily have been written self.blogs. Writing it with an implicit receiver is just shorter. Of course, blogs is also potentially ambiguous as the use of a local variable, but in this case the parser can tell it's not, since there's no local variable named blogs assigned previously in the method (and if there had been, a bare blogs would have the value of that local variable, and self.blogs would be necessary if you had meant the method call).
As for using #blogs = instead of self.blogs =, in this case it would have the same effect, but there is a subtle difference: if you later redefine the blogs= method to have additional effects (say, writing a message to a log), the call to self.blogs = will pick up those changes, whereas the bare direct access will not. In the extreme case, if you redefine blogs= to store the value in a database rather than an instance variable, #blogs = won't even be similar anymore (though obviously that sort of major change in infrastructure will probably have knock-on effects internal to the class regardless).
#variable will directly access the instance variable for that class. Writing self.variable will send to the object a message variable. By default it will return the instance variable but it could do other things depending on how you set up your object. It could be a call to a method, or a subclass, or anything else.
The difference between calling blogs or self.blogs is totally up to syntax. If you use an opinionated syntax checker like rubocop it will tell you that you have a redundant use of self

Is this define_method use case too complex?

I've been bashing my head against this for about three days now. I've created a class that models html pages and tells cucumber step definitions where to populate form data:
class FlightSearchPage
def initialize(browser, page, brand)
#browser = browser
#start_url = page
#Get reference to config file
config_file = File.join(File.dirname(__FILE__), '..', 'config', 'site_config.yml')
#Store hash of config values in local variable
config = YAML.load_file config_file
#brand = brand #brand is specified by the customer in the features file
#Define instance variables from the hash keys
config.each do |k,v|
instance_variable_set("##{k}",v)
end
end
def method_missing(sym, *args, &block)
#browser.send sym, *args, &block
end
def page_title
#Returns contents of <title> tag in current page.
#browser.title
end
def visit
#browser.goto(#start_url)
end
def set_origin(origin)
self.text_field(#route[:attribute] => #route[:origin]).set origin
end
def set_destination(destination)
self.text_field(#route[:attribute] => #route[:destination]).set destination
end
def set_departure_date(outbound)
self.text_field(#route[:attribute] => #date[:outgoing_date]).set outbound
end
# [...snip]
end
As you can see, I've used instance_variable_set to create the variables that hold the references on the fly, and the variable names and values are supplied by the config file (which is designed to be editable by people who aren't necessarily familiar with Ruby).
Unfortunately, this is a big, hairy class and I'm going to have to edit the source code every time I want to add a new field, which is obviously bad design so I've been trying to go a stage further and create the methods that set the variable names dynamically with define_method and this is what's kept me awake until 4am for the last few nights.
This is what I've done:
require File.expand_path(File.dirname(__FILE__) + '/flight_search_page')
class SetFieldsByType < FlightSearchPage
def text_field(config_hash)
define_method(config_hash) do |data|
self.text_field(config_hash[:attribute] => config_hash[:origin]).set data
end
end
end
The idea is that all you need to do to add a new field is add a new entry to the YAML file and define_method will create the method to allow cucumber to populate it.
At the moment, I'm having problems with scope - Ruby thinks that define_method is a member of #browser. But what I want to know is: is this even feasible? Have I totally misunderstood define_method?
Do you mean that you'd expect to see the requires and file loads outside the class definition?
No, inside the class definition. Ruby class declarations are just code that execute in the order it's seen. Things like attr_accessor are just class methods that happen to do things to the class being defined, as it's being defined. This seems like what you want to do.
In your case you'd read the YAML file instead, and run your own logic to create accessors, build any backing data required, etc. I don't totally grok the usecase, but it doesn't sound unusual or difficult--yet.
That said, how much "convenience" do you get by putting these definitions in a YAML file? Consider something like I did once to create page instances I used to drive Watir:
class SomePage < HeavyWatir
has_text :fname # Assumed default CSS accessor pattern
has_text :whatever, accessor: 'some accessor mechanism', option: 'some other option'
end
The has_xxx were class methods that created instance variable accessors (just like attr_accessor does), built up some other data structures I used to make sure all the things that were supposed to be on the page actually were, and so on. For example, very roughly:
page = SomePage.new
page.visit
if page.missing_fields?
# Do something saying the page isn't complete
end
It sounds like you want something vaguely similar: you have a bunch of "things" you want to give to the class (or a sub-class, or you could mix it in to an arbitrary class, etc.)
Those "things" have additional functionality, that functionality works in multiple ways, like:
Things-that-happen-during-definition
E.g., has_text adds the name to a class instance hash ofthe page's metadata, like field names.
Things-that-happen-during-usage
E.g., when fname= is called set a flag in the instance's metadata saying the setter was called.
This is an appropriate case for metaprogramming, but it looks like you're going about it the wrong way.
First of all, is there going to be a different config file for each instance of FlightSearchPage or just one config file that controls all pages? It looks like you're loading the same config file regardless of the arguments to initialize so I'm guessing your case is the former.
If that is so, you need to move all of your metaprogramming code into the class (outside method definitions). I.e. when the class is defined, you want it to load the config file and then each instance is created based on that config. Right now you have it reloading the config file every time you create an instance, which seems incorrect. For example, define_method belongs to Module so it should appear in class scope, rather than in an instance method.
On the other hand, if you do want a different config for each instance, you need to move all of your metaprogramming code into the singleton class e.g. define_singleton_method rather than define_method.

Spec RSpec model attribute setter

I'm using Sinatra (1.2) and RSpec (2.5) and would like to create a new object with an attribute TDD style. This is how the end result should look like:
class User
def initialize(name)
#name = name
end
end
I know I have to write the example before the implementation but I'm trying to explain my question here. :) Here is the not working spec I have so far:
describe User
it "creates a new user object" do
name = mock("A name")
user = mock(User) # shouldn't do this, see the reply's
user.should_receive(:name=).with(name)
User.new(name)
end
end
When I run RSpec I get the "expected: 1 time, received 0 times" error. Any idea how I can explain RSpec I would like to assign the name attribute?
Note: I'm not using Rails, not using ActiveRecord or anything, just Ruby.
First of all, let me explain why the spec you have written doesn't work:
You set an expectation that the mock object returned by mock(User) should receive name=. There are two problems with this. First of all the mock will receive nothing, because it is never called. mock(User) returns a mock object, and it cannot be used to set expectations for what the User class object will receive (to do that simply do User.should_receive(...)). Secondly, even if you had set the expectation on the User class object, that object will never receive name=. There are two reasons for this, too: firstly because name= (had it existed) would be an instance method, and as such not called on the class object, and secondly, you declare no name= instance method. What your code does is that it sets an instance variable.
Now, how should you write a test for this? You shouldn't. Tests are to define and assert the behaviour, not the implementation. Setting an instance variable is pure implementation. There is in your example code no way to get the value of the #name instance variable from outside of the class, therefore there is no reason to write a test for it.
Obviously your code is just an example, anything useful would do something with the #name variable, and that is what you should test. Start by writing a test for what a User object will be used for, then write all the implementation needed to fulfill that test (but no more). Write a test that reflects how the object will be used in actual production code.
I'd really recommend you don't approach this using mocks. It's not what they're for. In fact, specifying getters/setters like this is not really what TDD is for. The idea is to let a requirement drive the setters/getters into existence. For example, there might be a requirement that the user's name appear in a welcome message when he/she logs in. Then you might do something like this:
describe 'login process' do
it "displays user's name after successful login" do
user = User.new("Cimm", "cimm#somewhere.com", "secret")
post "/login", :email => "cimm#somewhere.com", :password => "secret"
last_response.body.should =~ /Welcome Cimm/m
end
end
This specifies behavior, and forces you to implement a means of setting the name attribute (via the constructor, in this case) and a means of accessing it. No need to specify the constructor directly.
Do you really want to mock the very object you are developing?
require 'rspec'
class User
attr_accessor :name
def initialize(name)
#name = name
end
end
describe User do
subject {User.new "other name"}
it "creates a new user object" do
subject.should respond_to :name=
end
end

Saving Dynamic Ruby Classes

I have a curiosity question. If I have a ruby class and then I dynamically add class methods, class variables, etc. to it during execution is there anyway for me to save the altered class definition so that next time I start my application I can use it again?
There is no built-in way to do this. Marshal can't save methods. If these methods and variables are generated in some systematic way, you could save the data necessary for the class to recreate them. For example, if you have a make_special_method(purpose, value) method that defines these methods, create an array of the arguments you need to pass to these methods and read it in when you want to reconstitute the state of the class.
Depending on what exactly you mean, there are a couple of ways to go about this.
The simplest case is the one where you've added variables or methods to an already-existing class, as in this example:
class String
def rot13
return self.tr('a-z', 'n-za-m')
end
end
Here we've added the rot13 method to class String. As soon as this code is run, every String everywhere in your program will be able to #rot13. Thus, if you have some code that needs rot13-capable strings, you just ensure that the code above is run before the code in question, e.g. by putting the rot13 code in a file someplace and require()ing it. Very easy!
But maybe you've added a class variable to a class, and you want to preserve not just its existence but its value, as in:
class String
##number_of_tr_calls_made = 0
# Fix up #tr so that it increments ##number_of_tr_calls_made
end
Now if you want to save the value of ##number_of_tr_calls_made, you can do so in the same way you would with any other serializable Ruby value: via the Marshal library. Also easy!
But something in the way you phrased your question makes me suspect that you're doing something like this:
greeting = "Hello"
class <<greeting
def rot13
return self.tr('a-z', 'n-za-m')
end
end
encrypted_greeting = greeting.rot13
This is very different from what we did in the first example. That piece of code gave every String in your program the power to rot13 itself. This code grants that power to only the object referred to by the name 'greeting'. Internally, Ruby does this by creating an anonymous Singleton subclass of String, adding the rot13 method to it, and changing greeting's class to that anonymous subclass.
The problem here is that Singletons can't be Marshal'd (to see why, try to figure out how to maintain the Singleton invariant when any call to Marshal.load can generate copies of extant Singleton objects). Now greeting has a Singleton in its inheritance hierarchy, so if you want to save and load it you are hosed. Make a subclass instead:
class HighlySecurableString < String
def rot13
return self.tr('a-z', 'n-za-m')
end
end
greeting = HighlySecurableString.new("hello")
Simply marshalling the object (as others have said) wont work. Lets look at an example. Consider this class:
class Extras
attr_accessor :contents
def test
puts "This instance of Extras is OK. Contents is: " + #contents.to_s
end
def add_method( name )
self.class.send :define_method, name.to_sym do
puts "Called " + name.to_s
end
end
end
Now lets write a program which creates an instance, adds a method to it and saves it to disk:
require 'extras'
fresh = Extras.new
fresh.contents = 314
fresh.test # outputs "This instance of Extras is OK. Contents is: 314"
fresh.add_method( :foo )
fresh.foo # outputs "Called foo"
serial = Marshal.dump( fresh )
file = File.new "dumpedExample", 'w'
file.write serial
So we can call the normal method 'test' and the dynamic method 'foo'. Lets look at what happens if we write a program which loads the instance of Example which was saved to disk:
require 'extras'
file = File.new 'dumpedExample', 'r'
serial = file.read
reheated = Marshal.load( serial )
reheated.test # outputs "This instance of Extras is OK. Contents is 314"
reheated.foo # throws a NoMethodError exception
So we can see that while the instance (including the values of member variables) was saved the dynamic method was not.
From a design point of view it's probably better to put all your added code into a module and load that into the class again when you next run the program. We'd need a good example of how you might want to use it though to really know this.
If you need extra information to recreate the methods then have the module save these as member variables. Implement included in the module and have it look for these member variables when it is included into the class.
http://ruby-doc.org/core/classes/Marshal.html
You're editing the class on the fly, and you want to save that? You could try using the Marshal module, it'll allow you to save objects to a file, and read them back in memory dynamically.

Resources