Ruby method and variable inheritance - ruby-on-rails

I'm trying to access variables defined in class One, through inheritance, in class Two. I can't seem to find the right way of going about it - it seems to work for methods:
class One
class << self
def test
puts "I'm a method from class one"
end
end
end
end
And as a new object the variable is accessible:
class Two < One
test
end
#=> I'm a method from class one
class Test
attr_accessor :a
def initialize
#a = "hi"
end
end
Test.new.a
#=> "hi"
But I'm trying to do something like:
class One
class << self
a = "hi"
end
end
class Two < One
a
end
#=> NameError: undefined local variable or method `a' for Two:Class
For now I'm using class variables, but I'm sure there's a better way:
class One
##a = "hi"
end
class Two < One
##a
end
#=> "hi"

local and class instance variables wouldn't be accessible through inheritance in Ruby.

Limosine is an example of a class inheriting, a variable (brand) and a method, to_s
class Car
def initialize(brand)
#brand = brand
end
def to_s
"(##brand, ##model)"
end
end
class Limosine < Car
def initialize(brand, model)
super(brand)
#model = model
end
end
Use:
puts Merc.new("Mercedes", "Maybach")to_s

Related

NoMethodError (undefined method) from Private method in class

Why can't I use a private method from within my class? How do I fix my code to prevent the error?
module CarRegistration
class Basics < Base
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
private
car_structure = #array of hashes
def fields_of_model(model)
car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
end
error
NoMethodError (undefined method `fields_of_model' for
CarRegistration::Basics:Class):
I think you have a number of problems going on here.
First, you've defined fields_of_model as an instance method, here:
def fields_of_model(model)
car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
but you're trying to call it from the class, here:
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
So, you'll want to make fields_of_model a class method, and define it before you call it. Something like:
module CarRegistration
class Basics < Base
private
car_structure = #array of hashes
class << self
def fields_of_model(model)
car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
end
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
end
You'll also have problems with that car_structure variable, I think, because it'll be out of scope for the class method. So, I think you need to make a class-level instance variable. So, give this a try:
module CarRegistration
class Basics < Base
#car_structure = #array of hashes
class << self
def fields_of_model(model)
#car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
private :fields_of_model
end
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
end
Note that I made the class method, :fields_of_models private using private :fields_of_model.
To demonstrate the whole thing, I ginned up this RSpec test:
require 'rails_helper'
class Car
attr_accessor *%w(
color
make
year
).freeze
end
module CarRegistration
class Basic
#car_structure = [
{model: :car, name: :color},
{model: :car, name: :make},
{model: :car, name: :year}
]
class << self
def fields_of_model(model)
#car_structure.select {|record| record[:model] == model}.map{|record| record[:name]}
end
private :fields_of_model
end
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
def car
#car ||= Car.new
end
end
end
RSpec.describe CarRegistration::Basic do
it "has :fields_of_model as a private class method" do
expect(CarRegistration::Basic.public_methods).not_to include(:fields_of_model)
expect(CarRegistration::Basic.private_methods).to include(:fields_of_model)
end
it "responds to :color and :color=" do
expect(car_registration).to respond_to(:color)
expect(car_registration).to respond_to(:color=)
end
it "sets and gets attributes on car" do
expect(car_registration.color).to be_nil
expect(car_registration.car.color).to be_nil
car_registration.color = :red
expect(car_registration.car.color).to eq(:red)
expect(car_registration.color).to eq(:red)
expect(car_registration.instance_variable_get(:#color)).to be_nil
end
end
def car_registration
#car_registration ||= described_class.new
end
Which, when run, yields:
CarRegistration::Basic
has :fields_of_model as a private class method
responds to :color and :color=
sets and gets attributes on car
Finished in 0.733 seconds (files took 27.84 seconds to load)
3 examples, 0 failures
BTW, having this code in your class outside of a def-end is just fine and not the root of your problem. In fact, it's quite normal.
Also, I will note that Jörg W Mittag wishes to say:
I am one of those Ruby Purists who likes to point out that there is no such thing as a class method in Ruby. I am perfectly fine, though, with using the term class method colloquially, as long as it is fully understood by all parties that it is a colloquial usage. In other words, if you know that there is no such thing as a class method and that the term "class method" is just short for "instance method of the singleton class of an object that is an instance of Class", then there is no problem. But otherwise, I have only seen it obstruct understanding.
Let it be fully understood by all parties that the term class method is used above in its colloquial sense.
Because you wrote the method not in def-end clause; you should write it like
def my_method
fields_of_model(:car).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: :car
end
end
That is why the error message says CarRegistration::Basics:Class as opposed to CarRegistration::Basics
Here is a sample code that works.
Usually there is no need to put a class inside Module, but if you must for some reason, this is a way.
module CarRegistration
class Basics < Object
def run(model)
fields_of_model(model)
end
private
def fields_of_model(model)
puts model
end
end
end
a = CarRegistration::Basics.new
a.run('xyz') # => 'xyz' is printed.

How to access class method from instance method in ruby on rails non activerecord model

I have a non activerecord rails model:
class Document
attr_accessor :a, :b
include ActiveModel::Model
def find(id)
initialize_parameters(id)
end
def save
...
end
def update
...
end
private
def initialize_parameters(id)
#a = 1
#b = 2
end
end
In order to find the Document, I can use:
Document.new.find(3)
So, to get it directly I changed the find method to
def self.find(id)
initialize_parameters(id)
end
And I get the following error when I run
Document.find(3)
undefined method `initialize_parameters' for Document:Class
How can I make this work?
You can't access an instance method from a class method that way, to do it you should instantiate the class you're working in (self) and access that method, like:
def self.find(id)
self.new.initialize_parameters(id)
end
But as you're defining initialize_parameters as a private method, then the way to access to it is by using send, to reach that method and pass the id argument:
def self.find(id)
self.new.send(:initialize_parameters, id)
end
private
def initialize_parameters(id)
#a = 1
#b = 2
end
Or just by updating initialize_parameters as a class method, and removing the private keyword, that wouldn't be needed anymore.
This:
class Document
attr_accessor :a, :b
def self.find(id)
initialize_parameters(id)
end
end
Is not trying to "access class method from instance method" as your title states. It is trying to access a (non-existent) class method from a class method.
Everything Sebastian said is spot on.
However, I guess I would ask: 'What are you really trying to do?' Why do you have initialize_parameters when ruby already gives you initialize that you can override to your heart's content? IMO, it should look something more like:
class Document
attr_accessor :a, :b, :id
class << self
def find(id)
new(id).find
end
end
def initialize(id)
#a = 1
#b = 2
#id = id
end
def find
# if you want you can:
call_a_private_method
end
private
def call_a_private_method
puts id
end
end

What is this syntax inside ruby clases?

class ApplicationController < ActionController::Base
protect_from_forgery #What is this syntax? When is this executed and how to create one?
end
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :body, :commenter, :post
end
In the first case, I understand ApplicationController is a new Derived class of class called Base in the module ActionController. What happens in the next line? Is protect_from_forgery a method in base class or in module ActionController? What is it called? I couldn't find in ruby classes documentation. I tried creating a method in base class but got errors like below. How do I create such special commands which can be used in a class?
class Base
def foo
#name = "foo"
end
end
class Der < Base
foo
def bar
#dummy = "bar"
end
end
Error:
expr1.rb:62:in `<class:Der>': undefined local variable or method `foo' for Der:Class (NameError)
from expr1.rb:61:in `<main>'
protect_from_forgery is a class-method defined in one of the modules included in ActionController::Base and made available to the child class when you inherit from ActionController::Base.
This kind of methods in Rails are sometimes called "macro" as they are class methods that enables some specific feature (sometimes also using metaprogramming to define extra methods or helpers). In reality, the term "macro" is incorrect as Ruby has no macro. They are nothing else than class methods.
The most important detail to keep in mind is that, when they are used in the class-definition. these methods are run at code-evaluation and not at runtime.
class Base
def foo_instance
p "foo instance"
end
def self.foo_class
p "foo class"
end
end
class Der < Base
foo_class
def bar
p "bar"
end
end
Der.new.bar
will produce
"foo class"
"bar"
To create class methods you can do either of these.
class Base
def self.foo
end
end
class Base
class << self
def foo
end
end
end
Because they are class methods you call them on the class
Base.foo
So what you're talking about are "class methods" - class methods are methods that are defined on the class itself, not on the instance of the class. Note the following:
class Greeter
def initialize(name)
#name = name
end
def greet
"hello, #{#name}"
end
end
Greeter.new("bob").greet # => "hello, bob"
#greet is an instance method on the Greeter classes. However, .new is a "class method" - which is a method called on the class itself. Attempting to call .greet on the Greeter class would result in a NameError:
Greeter.greet # ! NameError
So if you want to define such a "class method", you have to use one of the following syntaxes:
class Greeter
def self.greet(name)
"hello, #{name}"
end
class << self
def greet(name)
"hello, #{name}"
end
end
class << Greeter
# same as above
end
end
def Greeter.greet(name)
"hello, #{name}"
end
Going back to the original question, if you reopen the greeter class, you can now use the .greet method:
class Greeter
greet "bob" # => "hello, bob"
end
This also applies for subclassing, as well:
class Host < Greeter
greet "bob" # => "hello, bob"
end
This is how Rails provides these methods - they define class methods on the base class, most often ActiveRecord::Base, which you can then use - explaining methods such as protect_from_forgery.
protect_from_forgery is a class method:Could look like this
def protect_from_forgery(options = {})
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
prepend_before_action :verify_authenticity_token, options
append_after_action :verify_same_origin_request
end
So its inherited by application controller:
class ApplicationController < ActionController::Base
Your example could be written like this:
class Base
class << self
def foo
#name = "foo"
end
end
end
class Der < Base
foo #class method from Base
def bar
#dummy = "bar"
end
end
To see foo's values
Der.foo.inspect
Its inheritance. In your child class you can use all method inhertited from parrent class.
Firstly you can call methods on class instance.
In your example you can do sth like this:
base_object = Base.new
base_object.foo
der_object = Der.new
der_object.bar
but also thanks to inheritance you can do sth like this:
der_object.foo
Here is simple tutorial for ruby inheritance with examples:
http://rubylearning.com/satishtalim/ruby_inheritance.html
happy coding!

How does syntax like "has_many" interact with the Class objects?

My goal is to implement something like "has_many" in Rails' ActiveRecord. I want to understand more about how the classes/objects are loaded.
What is happening when I use "has_many" in the following?
class Widget < MiniRecord
has_many :trinkets
end
Let's say that this is the implementation of "MiniRecord":
class MiniRecord
class << self
def has_many associated_model
#associations[:has_many] << associated_model_sym
end
end
def list_associations
do_stuff(#associations) # Where do I instantiate #associations?
end
end
I've gotten it to the point where it works, but I have some messy code that I'd like to trim down.
I want to understand how this works, but specifically, where should I instantiate #associations?
I want to be able to "declare" attributes in subclasses of MiniRecord that is defined when the class is defined (i.e. a static variable), and then act on these attributes later.
When you extend MiniRecord, say MyRecord, you got a class hierarchy figure something like the following (#SomeClass is eigenclass or singleton class of SomeClass):
Class
|
MiniRecord --class--> #MiniRecord
| |
MyRecord --class--> #MyRecord
Where the list_associations method is defined on MiniRecord and has_many is defined on #MiniRecord.
When you call has_many in MyRecord's class body, it will first look up the method in #MyRecord, which will fail. Then it will look up in #MiniRecord and found the definition. If it fails somehow, it will look in Class for the definition. That's how this approach works.
For the question
specifically, where should I instantiate #associations?
You have two choice:
initialize when used
def has_many associated_model
#associations ||= Hash.new{|h, k| h[k] = [] }
#associations[:has_many] << associated_model_sym
end
initialize when the descendant class get created
class MiniRecord
def self.inherited klass
klass.instance_variable_set(:#associations, Hash.new{|h, k| h[k] = [] }
end
end
Note that you cannot access #associations in instance method list_associations directly, because it's in different scope. You need to expose #associations on MiniRecord and then get the value from there.
class MiniRecord
class << self
def has_many associated_model
#associations[:has_many] << associated_model
end
attr_reader :associations
end
def self.inherited klass
klass.instance_variable_set(:#associations, Hash.new{|h, k| h[k] = [] })
end
def list_associations
do_stuff(self.class.associations) # Where do I instantiate #associations?
end
def do_stuff sth
p sth
end
end
class MyRecord < MiniRecord
has_many :thrinks
end
r = MyRecord.new
r.list_associations
p MyRecord.associations

how to set an instance variable from a class method?

I am trying to do a custom active record macro. But it right now seems impossible set an instance variable from within it's block.. here is what i am trying to do.
module ActiveRecord
class Base
def self.included(base)
base.class.send(:define_method, :my_macro) do |args|
# instance_variable_set for the model instance that has called this
# macro using args
end
end
end
end
i have tried class_eval, instance_eval.. but nothing seems to work or i don't how to use them.
Thanks in advance.
Edit: Let me try to explain better. I have a class method. An instance of the class calls this method. Now, this class method should instruct the instance to set an instance variable for itself.
Edit- this is how i want o use the macro
class MyModel < ActiveRecord::Base
my_macro(*args)
def after_initialize
# use the value set in the macro as #instance variable
end
end
Is this what you are thinking of:
class DynamicAdd
def add_method
self.class_eval do
attr_accessor :some_method
end
end
end
You can then do the following:
k = DynamicAdd.new
k.some_method = "hi"
should result in an undefined method error.
But,
k = DynamicAdd.new
k.add_method
k.some_method = "hi"
should work.
You can use this same format to define other types of methods besides attr_accessors as well:
class DynamicAdd
def add_method
self.class_eval do
def some_method
return "hi"
end
end
end
end
Hm.. Isn't included() a Module method? I don't think you can use that in a class like you have written. If you want to create a class method you can do
class Base
def self.my_method
end
or
class Base
class << self
def my_method
end
end
If all you want to do is to add an instance variable to an existing object, then you can use #instance_variable_set
class Base
class << self
def my_method(instance_of_base, value)
instance_of_base.instance_variable_set "#x", value
end
end
end
a = Base.new
a.class.send(:my_method, *[a,4])

Resources