Rails 4: having a proxy model to combine multiple models - ruby-on-rails

I am trying my hand on rails (4). I have done some Sinatra earlier.
I have a signup form, in which user can fill out his organization name, address and his own name, password etc. I have two tables - Users and Organizations, those table get populated with Signup data. So, I have two active records model users and organizations. My controllers looks as follows:
class SignupsController < ApplicationController
# GET /signup
def new
# show html form
end
# POST /signup
def create
#signup = Registration.register(signup_params)
respond_to do |format|
if #signup.save
format.html { redirect_to #signup, notice: 'Signup was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def signup_params
params[:signup]
end
end
I do not have any Registration model (table in db). What I am looking for such Registration model (should I call it model?) where I can have something like:
class Registration
def self.register params
o = Organization.new params
u = User.new o.id, params
self.send_welcome_email params[:email]
end
def send_welcome_email email
#send email here
end
end
1) So where should I keep this Registration class?
2) Is this a correct approach for such situation? If not, then what is the best way to do it?
3) Having such class will effect running any unit tests?
4) I see there is file, signup_helper.rb what is the use of that in SignupsController

You can do include ActiveModel::Model in your model, and it will behave as a normal Model. You will be able to do validations, callbacks. Anything other than persisting to a database.
Have a look at this for more info.

class Registration
include ActiveModel::Model
def self.register params
o = Organization.new params
u = User.new o.id, params
self.send_welcome_email params[:email]
end
def send_welcome_email email
#send email here
end
end

To answer your questions:
1) I would move the Registration class to a Signup module (as it relates to the signup use case) in app/models:
# apps/models/signup/registration.rb
module Signup
class Registration
...
end
end
2) I like your approach. It's something I would do. It is not the "Rails way" but in my opinion it is superior.
3) No. Having a PORO (plain old ruby object) is currently fine for any unit tests and you can easily test it.
4) Helpers are an (in my opinion) old and obsolete way to share functionality between views (and controllers). Don't use them unless there is absolutely no other way (maybe some Library demands it ... ). It is alway better to use POROs like you did.
You don't have to include ActiveModel::Model unless you need it's functionality. E.g. validations. You can include individual pieces of its functionality in this case. E.g
include ActiveModel::Validations

Related

How to I execute a function only if a condition of an object's relation is met in ruby/rails?

My question is probably not very clear and probably best illustrated with an example... I have an account model which has many users (which belongs to account). I also have a a method in a mailer that I only want to run if the account that the user is related to has a state of "active". Basically, the user should only be sent the email if there account is active. The method in the mailer file looks like this at the moment.
def pending_mail(document, user)
#user = user
mail(to: user.email, subject: t('emails.pending.subject') ... do |format|
format.text
format.html
end
end
The mailers only job is to render and send emails - not to handle business logic. Its not the mailers job to decide who gets an email or not.
This should be handled in the controller. For example:
class AccountsController < ApplicationController
def update
if #account.update(account_attributes)
Accounts::UsersNotificationJob.perform_later if #account.active?
redirect_to #account
else
render :new
end
end
end
module Accounts
class UsersNotificationJob
def perform(account)
account.users.each do |user|
UserMailer.pending_mail(user).deliver
end
end
end
end
You should have access the owning account through user.account, depending on how your model is defined. In which case you can just use a simple if statement.
if #user.account.state == "active"
mail blabla do |format|
.....
end
end

Multiple forms in Rails

Not sure whether my database architecture is correct for rails. However below is my database architecture
Database Relations
Each User instance has only one PhoneBook instance.
A single Phonebook instance can have multiple Contact instances
A single Contact instance can have multiple Mobile instances
A single Contact instance can have multiple Email instances
The question is how should I implement my controller and views if I want to add a new contact for a signed in user in his phonebook.
you can do that with accepts_nested_attributes_for:, like a nested form
you could define the current user like so
controllers/application_controller.rb
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
# or find_by_authtoken!(...)
end
then you could do
controllers/phonebooks_controller.rb
def create
#phonebook = Phonebook.create(phonebook_params)
if #phonebook.save
# redirects here
end
end
.....
def phonebook_params
params.require(:phonebook).permit(:phonebook_params....).merge(:user_id => current_user)
end
and in your contacts controller
controllers/contacts_controller.rb
def create
#contact = Contact.create(contact_params)
if #contact.save
# redirects here
end
end
.....
def contact_params
params.require(:contact).permit(:contact_params....).merge(:user_id => current_user, :phonebook_id => current_user.phonebook)
end
Like that, you can use your forms in a simple manner, without having to generate routes like /user/id/phonebook/id/contacts
in addition to the links below the first answer, maybe have a look at this basic form. It it is not a direct answer to your question, but maybe it'll help you getting an idea of how a form could look like.

where does this param symbol come from in Rails session

I'm reading Rails 3 Way by Obie Fernandez. He's demonstrating the use of a plugin Authlogic, and created a User and UserSession model and a UsersController and UserSessionsController.
He hasn't created any views (but he might assume some exist)
In the UserSessionsController, he creates this code
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
redirect_to user_path(current_user)
else
render :action => :new
end
end
def destroy
current_user_session.destroy
redirect_to new_user_session_path
end
end
My question relates to the create method. When he writes
UserSession.new(params[:user_session])
where is :user_session coming from? I undersdtand that UserSession.new instantiates a new object, but where do the params come from? and what names would they have?
Does it depend on something in the imaginary view? or are these params automatically generated by Rails based on the name of the Models?
params is a special hash that is passed to all actions, regardless of the type. If a given action has no parameters, then it's simply empty. It's how you can pass parameters from a page/form/URL parameters into the action. One of the most common sources of parameters are data elements from a form.
In the case of authlogic, it contains user credentials for creating the user session (username, password).
Check out the parameters section for more information.

Accessing a variable declared in a controller from a model

I'm using the facebooker gem which creates a variable called facebook_session in the controller scope (meaning when I can call facebook_session.user.name from the userscontroller section its okay). However when I'm rewriting the full_name function (located in my model) i can't access the facebook_session variable.
You'll have to pass the value into your model at some point, then store it if you need to access it regularly.
Models aren't allowed to pull data from controllers -- it would break things in console view, unit testing and in a few other situations.
The simplest answer is something like this:
class User
attr_accessor :facebook_name
before_create :update_full_name
def calculated_full_name
facebook_name || "not sure"
end
def update_full_name
full_name ||= calculated_full_name
end
end
class UsersController
def create
#user = User.new params[:user]
#user.facebook_name = facebook_session.user.name
#user.save
end
end

Where do I put 'helper' methods?

In my Ruby on Rails app, I've got:
class AdminController < ApplicationController
def create
if request.post? and params[:role_data]
parse_role_data(params[:role_data])
end
end
end
and also
module AdminHelper
def parse_role_data(roledata)
...
end
end
Yet I get an error saying parse_role_data is not defined. What am I doing wrong?
Helpers are mostly used for complex output-related tasks, like making a HTML table for calendar out of a list of dates. Anything related to the business rules like parsing a file should go in the associated model, a possible example below:
class Admin < ActiveRecord::Base
def self.parse_role_data(roledata)
...
end
end
#Call in your controller like this
Admin.parse_role_data(roledata)
Also look into using (RESTful routes or the :conditions option)[http://api.rubyonrails.org/classes/ActionController/Routing.html] when making routes, instead of checking for request.post? in your controller.
Shouldn't you be accessing the parse_role_data through the AdminHelper?
Update 1: check this
http://www.johnyerhot.com/2008/01/10/rails-using-helpers-in-you-controller/
From the looks of if you're trying to create a UI for adding roles to users. I'm going to assume you have a UsersController already, so I would suggest adding a Role model and a RolesController. In your routes.rb you'd do something like:
map.resources :users do |u|
u.resources :roles
end
This will allow you to have a route like:
/users/3/roles
In your RolesController you'd do something like:
def create
#user = User.find_by_username(params[:user_id])
#role = #user.roles.build(params[:role])
if #role.valid?
#role.save!
redirect_to #user
else
render :action => 'new'
end
end
This will take the role params data from the form displayed in the new action and create a new role model for this user. Hopefully this is a good starting point for you.

Resources