attr_accessor, not able to access property
Looks like group_id
is already a property on your Foo
object (shown by the fact that it returns 106 when attr_accessor
is omitted). By adding attr_accessor
you are overriding what's already there and creating a method reader and writer called group_id
. The return of the newly defined group_id
is nil since you don't define anything.
Conceptually, you're ending up with something like this:
class Foo < ActiveRecord::Base
def group_id # overriding previous definition of 'group_id'
nil
end
end
Edit:
If your goal is expose properties then yes, use attr_accessible
methods created by attr_accessor are not available to sub classes
What you're looking for is cattr_accessor which fixes this specific problem:
http://apidock.com/rails/Class/cattr_accessor
Here's your example, fixed:
class Human
def self.age
@age = 50
end
def self.age=(input)
@age = input
end
cattr_accessor :name
self.name = 'human'
end
class Boy < Human
end
puts Human.age
puts Boy.age
puts Human.name
puts Boy.superclass.name
puts Boy.name # => 'human'
Property from attr_accessor does not show when I render json
render json: user, methods: [:score]
attr_accessor is alternative for getter and setter method so it is a method and as I have mentioned we can call it as above
Why can't I use attr_accessor inside initialize?
You can't call attr_accessor on the instance, because attr_accessor is not defined as an instance method of MyClass. It's only available on modules and classes. I suspect you want to call attr_accessor on the instance's metaclass, like this:
class MyClass
def initialize(varname)
class <<self
self
end.class_eval do
attr_accessor varname
end
end
end
o1 = MyClass.new(:foo)
o2 = MyClass.new(:bar)
o1.foo = "foo" # works
o2.bar = "bar" # works
o2.foo = "baz" # does not work
What is attr_accessor in Ruby?
Let's say you have a class Person
.
class Person
end
person = Person.new
person.name # => no method error
Obviously we never defined method name
. Let's do that.
class Person
def name
@name # simply returning an instance variable @name
end
end
person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error
Aha, we can read the name, but that doesn't mean we can assign the name. Those are two different methods. The former is called reader and latter is called writer. We didn't create the writer yet so let's do that.
class Person
def name
@name
end
def name=(str)
@name = str
end
end
person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"
Awesome. Now we can write and read instance variable @name
using reader and writer methods. Except, this is done so frequently, why waste time writing these methods every time? We can do it easier.
class Person
attr_reader :name
attr_writer :name
end
Even this can get repetitive. When you want both reader and writer just use accessor!
class Person
attr_accessor :name
end
person = Person.new
person.name = "Dennis"
person.name # => "Dennis"
Works the same way! And guess what: the instance variable @name
in our person object will be set just like when we did it manually, so you can use it in other methods.
class Person
attr_accessor :name
def greeting
"Hello #{@name}"
end
end
person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"
That's it. In order to understand how attr_reader
, attr_writer
, and attr_accessor
methods actually generate methods for you, read other answers, books, ruby docs.
Using attr_accessor and attr_accessible on the same field
Thanks everyone for quick answers!
Your answers combined gave me the pieces I needed to understand this puzzle, I think.
(In a related problem, I was getting a lot of nil errors like "Object doesn’t support #inspect", and "undefined method ‘keys’ for nil:NilClass". I managed to solve it now, by removing the att_accessor field altogether.)
By experimenting with this particular case, this is what I've found out:
Actually, the :name field won't be persisted to the database.
user = User.new(:name=>"somename")
Will only set the attribute on the object, but not persist the :name column to the database. Like the following 'rails console' output shows:
> user
=> <User id: nil, created_at: nil, updated_at: nil>
> user.save
=> true
> user
=> <User id:1, created_at: 2011-01-19 12:37:21, updated_at: 2011-01-19 12:37:21>
I assume this is because *the setter made by attr_accessor will override ActiveRecord's setter* (which takes care of the database persistence). You can still retrieve the value from the :name field from the object though, like this:
> user.name
=> "somename"
So, in conclusion, I've learnt that using attr_accessor on fields might lead to them not being persisted to the database. And while I thought attr_accessible describes fields in the database that should be accessible from the outside, it doesn't seem to make a difference in this case.
Why attr_accessor won't work in setting methods
It's actually totally OK to use instance variables within instance methods. In fact, that's what they're for! Setters and getters allow things outside the instance to access variables inside the instance. They (basically) define instance methods for the class like this:
class Foo
# getter -- Same as attr_reader :root
def root
@root
end
# setter -- Same as attr_writer :root
def root=(root)
@root = root
end
# attr_accessor defines a setter *and* a getter.
end
So, you could simplify your code by defining #insert
so that it only takes one argument (value
) and replace every place where you reference node
with a reference to @root
.
The way that I think you're looking for (but is not the "right" way, and I wouldn't recommend) is to call the root
and root=
methods defined by the accessor.
If you took this route, you'd also have to define #insert
to only take value
as an argument and replace every place that you reference node
with root
. This will work, but it's not the right way to solve the problem. If you solve it this way, please ask a question on CodeReview.se so I can clarify how you can make the code better.
Why it's not working
In the #insert method, you're manipulating the node
parameter that was passed to the method, not root
. Ruby is pass by value not pass by reference (sorta), so when you pass mybst.root
to #insert
, you're effectively passing nil
because mybst.root == nil
. Then the mybst.insert
call returns a new Node, but you don't do anything with that return value. If you wanted to set root
to that return value, you could do:
mybst = BST.new
p mybst.root
mybst.root = mybst.insert(1, mybst.root)
mybst.root = mybst.insert(2, mybst.root)
mybst.root = mybst.insert(10, mybst.root)
mybst.root = mybst.insert(12, mybst.root)
p mybst
Explanation of what that textbook is trying to say
I think that the confusing part here is where the textbook says:
Hide the variables, even from the class that defines them
This is correct, but I think you're misinterpreting it. This section is saying that you should hide instance variables from anything outside of that instance. Within that instance, it's totally OK to use them, and that's actually the reason why instance variables exist -- to store state within the instance. It's just considered better to define methods for behaviors rather than directly exposing the instance variables. Of course, this is just one rule to keep in mind -- I'm sure you'll come across a situation where this advice does not apply, but generally you want to keep instance variables internal.
Why do we need attr_accessor?
Because it makes the code easier to maintain.
This way its clear from reading the class code what you expect to have happen, and that you expect other classes to access these variables.
Without this outside code could simply access your internal variables, and then if you refactor your code and remove or rename such a variable the outside code breaks.
Thus the accessor methods make it clear you intend for other classes to access these methods
FYI: Ruby is very powerful, and if you want you can go ahead and make it work the way you want. I don't recommend this but am pointing it out so you will understand that its a explicit choice being made for the good of keeping your code readable. But if you want to see how you could do this read on, and try running the code...
<script type="text/ruby">
def puts(s) # ignore this method, just dumps to the display Element['#output'].html = Element['#output'].html + s.to_s + "<br/>"end
class OpenKimono def method_missing(name, *args) if name =~ /=$/ instance_variable_set("@#{name[0..-2]}".to_sym, args[0]) else instance_variable_get("@#{name}") end endend
class MyClass < OpenKimonoend
foo = MyClass.new
foo.bar = 12
puts "foo.bar just set to 12, it now = #{foo.bar}"
foo.baz = 13
puts "foo.baz just set to 13, it now = #{foo.baz}"
puts "foo.manchu has never been set... what does it equal? #{foo.manchu}"
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script src="https://rawgit.com/reactive-ruby/inline-reactive-ruby/master/inline-reactive-ruby.js"></script><div id="output" style="font-family: courier"></div>
Cannot retrieve attr_accessor in Rails 4
I wouldn't override the create_merchant method if I were you, try something like this:
after_create -> { create_merchant!(:name => self.merchant_name) }
and make sure your merchant_name is permitted in the users controller, something like this:
def create
@user = User.new(user_params)
if @user.save
redirect_to @user
else
render :new
end
end
private
def user_params
params.require(:user).permit(:merchant_name, :email, :password, :password_confirmation)
end
Related Topics
Selenium Webdriver Take Screenshot of Viewport Only
Why Does 'Defined' Return a String or Nil
Routing Error During "Ruby on Rails-Tutorial"
How to Require Activerecord in Irb
Prawn Gem: How to Create the .Pdf from an *Existing* File (.Xls)
Reply to Thread Google-Api-Ruby-Client
Access Image from Different View in a View with Paperclip Gem Ruby on Rails
Heroku_Can't Upgrade to Cedar-14
Use Variable in Parameter Ruby on Rails
Connection Refused Using Sunspot and Solr in Rails
How to Escape All Characters with Special Meaning in Regex
Custom Gem Execution Fails with Nomethoderror
Heroku-18: Git Push Fails. Showing Different Versions of Ruby on Push
Use Some Middleware Only for Specific Rack Website
How to Install Version Specified Ruby Using Apt