How to call methods from two separate models in Ruby Volt - ruby

I have one model (Group) associated with my GroupController, but it has many instances of another model (People) in it. I want to be able to create new instances of People from my GroupController and add it to an array. What is the best way to go about doing this? The following is an excerpt from the GroupController:
class Group < Volt::ModelController
field :people_in_group
def add_people(name)
people_in_group = []
person = People.new(name)
people_in_group << person
end
end
The function breaks when I try to create a new person. Any advice?

Is this supposed to be a model? If so it should inherit from Volt::Model not Volt::ModelController
Thanks!

Try something like this:
class Person < Volt::Model
field :name, String
def initialize(a_name)
#name = a_name
end
end
class Group < Volt::Model
has_many :people
def add_people(name)
people << Person.new(name)
end
end

Related

Create instance of a new class object, from within separate class object

Is it possible in Ruby to instantiate another object of a different class using a class method?
I have spent a lot of time to no avail researching Google, the Ruby docs, and Stack Overflow, for an answer to my query, so I am posting this question as a last resort.
Two classes, User, and Blog. In User below I am trying to create a new instance of an object for Blog, and carry over some attributes.
In class User there are 2 methods;
class User
attr_accessor :blogs, :username
def initialize(username)
self.username = username
self.blogs = []
end
def add_blog(date, text)
self.blogs << [Date.parse(date),text]
new_blog = [Date.parse(date),text]
Blog.new(#username,date,text)
end
end
Using the above add_blog I would like to initialize & send a new object to the Blog class.
class Blog
attr_accessor :text, :date, :user
def initialize(user,date,text)
user = user
date = date
text = text
end
end
Yes, it is possible to instantiate another object of a different class using a class method. We ruby programmers are doing it all the time.
Do you want to have the blogs attribute of the user to have an array of blogs? Because your code just puts a date-text tupel into the array.
I think you wanted to do something like this in your user class:
def add_blog(date, text)
parsed_date = Date.parse(date)
new_blog = Blog.new(#username, parsed_date, text)
self.blogs << new_blog
new_blog
end
I have showed it step after step, but you can combine several lines. The last line returns the new blog, you may not need it if you only want the blog be part of the blogs array.
Looks like your code has some flaws. Here is the corrected code:
You were not using # to assign values to instance variables, you were putting a date in #blogs, instead of Blog object.
If you want to pass instance of User to Blog from add_blog, you can use self which represents current instance of User class. If you want to carry just some attributes, then, you can do so by referring to attributes using #attribute_name or self.attribute_name syntax
require "date"
class User
attr_accessor :blogs, :username
def initialize(username)
#username = username
#blogs = []
end
def add_blog(date, text)
#blogs << Blog.new(#username, Date.parse(date),text)
self
end
def to_s
"#{#username} - #{#blogs.collect { |b| b.to_s }}"
end
end
class Blog
attr_accessor :text, :date, :user
def initialize(user,date,text)
#user = user
#date = date
#text = text
end
def to_s
"Blog of #{user}: #{#date} - #{#text}"
end
end
puts User.new("Wand Maker").add_blog("2015-08-15", "Hello")
# => Wand Maker - ["Blog of Wand Maker: 2015-08-15 - Hello"]

Sorting An Array of Object by Different Attributes

I have an array of objects containing three different models. Two of them have a common attribute of category, and a third one that we just added doesn't. I'm wanting to sort the array alphabetically using the category name for the first two and the object's name for the third. Since they are all strings, this seems possible, but I don't know how to make it work.
My controller:
def index
#opportunities = Opportunity.non_historical_active.order("title")
#recommendations = Recommendation.active
#fast_content = FastContent.where(:approved => true)
#skills = (#recommendations + #opportunities + #fast_content)
Array displayed in my view:
<% Array(#skills.sort_by{ |skill| skill.opportunity_category.name}).each_with_index do |opp, i| %>
This array worked before we added the #fast_content variable to #skills.
Assuming Opportunity and Recommendation should be sorted by opportunity_category.name and FastContent should be sorted by name, this would work:
#skills.sort_by { |skill|
case skill
when Opportunity, Recommendation
skill.opportunity_category.name
when FastContent
skill.name
end
}
Another option is to add a common method to all three classes:
class Opportunity < ActiveRecord::Base
def sort_name
opportunity_category.name
end
end
class Recommendation < ActiveRecord::Base
def sort_name
opportunity_category.name
end
end
class FastContent < ActiveRecord::Base
def sort_name
name
end
end
And use that instead:
#skills.sort_by(&:sort_name)

Setting instance variables on different objects of the same class

Let's say I have a class called Person and person has an attribute called partner. When I call partner= on one of the Person objects, I want to set the #partner instance variable of both objects. Here's an example with invalid syntax:
class Person
attr_reader :partner
def partner=(person)
# reset the old partner instance variable if it exists
partner.#partner = nil if partner
# set the partner attributes
#partner = person
person.#partner = self
end
end
Change the attr_reader to an attr_accessor and add a helper method:
class Person
attr_accessor :partner
def link_partners(person)
#partner = person
person.partner = self
end
end
Update for visibility. Based on suggestion from Frederick below. This is a little more verbose, but will prevent partner from being set directly:
class Person
protected
attr_writer :partner
public
attr_reader :partner
def link_partners(person)
#partner = person
person.partner = self
end
end
Both implementations works like this:
p1, p2 = Person.new, Person.new
p1.link_partners(p2)
# p2.link_partners(p1)
You could provide a protected helper method which gets called by your partner= method to do the actual work. Since it can't be called by "outsiders", all of your check and balances can be maintained in your implementation of partner=:
class Person
attr_reader :partner
def partner=(person)
#partner.set_partner(nil) if #partner
set_partner(person)
person.set_partner(self) if person
end
def set_partner(person)
#partner = person
end
protected :set_partner
end
Never mind, I just discovered instance_variable_set.
class Person
attr_reader :partner
def partner=(person)
# reset the old partner instance variable if it exists
partner.instance_variable_set(:#partner, nil) if partner
# set the partner attributes
#partner = person
person.instance_variable_set(:#partner, self)
end
end
In theory, you could do this as follows:
def partner=(person)
#partner = person
person.instance_variable_set(:#partner, self)
end
However, I would consider this to be magic. (That's not a good thing.) Instead, make the attr_reader into an attr_accessor and write a different method to set two persons' partners to each other.
This will solve your issue with recursive set and looks better than your solution:
class Partner
attr_reader :partner
def set_partner(person, recursive = true)
# reset previous partner
#partner.set_partner(nil, false) if recursive && #partner
# set new partner
#partner = person
#partner.set_partner(self, false) if recursive
end
alias_method :partner=, :set_partner
end

Rails Model without a table

I want to create a select list for lets say colors, but dont want to create a table for the colors. I have seen it anywhere, but can't find it on google.
My question is: How can I put the colors in a model without a database table?
Or is there a better rails way for doing that?
I have seen someone putting an array or a hash directly in the model, but now I couldn't find it.
class Model
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :whatever
validates :whatever, :presence => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
attr_accessor will create your attributes and you will create the object with initialize() and set attributes.
The method persisted will tell there is no link with the database. You can find examples like this one:
http://railscasts.com/episodes/219-active-model?language=en&view=asciicast
Which will explain you the logic.
The answers are fine for 2013 but since Rails 4 all the database independent features of ActiveRecord are extracted into ActiveModel. Also, there's an awesome official guide for it.
You can include as many of the modules as you want, or as little.
As an example, you just need to include ActiveModel::Model and you can forgo such an initialize method:
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
Just use:
attr_accessor :name, :age
The easiest answer is simply to not subclass from ActiveRecord::Base. Then you can just write your object code.
What worked for me in Rails 6:
class MyClass
include ActiveModel::Model
attr_accessor :my_property
end
If the reason you need a model without an associated table is to create an abstract class real models inherit from - ActiveRecord supports that:
class ModelBase < ActiveRecord::Base
self.abstract_class = true
end
If you want to have a select list (which does not evolve) you can define a method in your ApplicationHelper that returns a list, for example:
def my_color_list
[
"red",
"green",
"blue"
]
end

List only ActiveRecord subclass methods

Given the following ActiveRecord model:
class User < ActiveRecord::Base
has_many :games
def name
"Joe"
end
def city
"Chicago"
end
end
I'd like to retrieve a list of the methods I added directly to the User class (and not those added by extending ActiveRecord and/or adding associations). Example output:
["name","city"]
Calling User.instance_methods(false) returns method added by ActiveRecord:
["validate_associated_records_for_games", "games", "game_ids", "games=", "game_ids=", "after_create_or_update_associated_records_for_games", "before_save_associated_records_for_games"]
Along with any model attributes from database columns. I'd like to exclude those and just get the custom methods on the subclass.
My purpose is method tracing: I'd like to trace my custom methods while excluding those added by ActiveRecord.
Any ideas?
User.instance_methods - ActiveRecord::Base.instance_methods #=> [:name,:city]
UPDATE:
the order of these methods are significant
class User < ActiveRecord::Base
def self.my_own_methods
self.instance_methods - ##im
end
has_many :games
##im = self.instance_methods
def name
"Joe"
end
def city
"Chicago"
end
end
User.my_own_methods #=> [:name, :city]
This one tested and it works
Use a method_added hook which adds method names to a list, and monkey patch Active Record so that methods added by AR are not added to that list.
If you don't want to crack AR open and start poking around, you could also define a "class macro" which defines a method and adds it to the list. For your own custom methods, use the class macro rather than def.
If you're not familiar with what I'm referring to as a "class macro", it's simply a method like this:
class Class
def mydef(name,&block)
(#methods || []) << name
define_method(name,&block)
end
end
Using something like mydef to define methods rather than def is definitely ugly, but it would solve the problem without requiring any monkey-patching.

Resources