In my Rails app there is a view with a simple user form consisting of a text box and a submit button.
When the user submits the form, depending on his input, different models are created:
class MessageController < ApplicationController
def create
if is_foo params[:text]
Foo.create
else
Bar.create
end
end
def is_foo(text)
# Here the message gets parsed
# i.e if text[0] == "M"
end
end
My question is, do you think that it's a better design to put the "is_foo" logic inside the Foo model instead of the controller like so?
Model:
class Foo < ActiveRecord::Base
def self.is_foo(text)
# Here the message gets parsed
# i.e if text[0] == "M"
end
end
Controller:
class MessageController < ApplicationController
def create
if Foo.is_foo params[:text]
Foo.create
else
Bar.create
end
end
end
On one hand, the model should take care of the logic. On the other, this isn't really logic, its more of an input rule... What do you think guys?
Helper
I'd leave the is_foo out of the model, as model logic should be to do with the model directly, not determining which model should be created / saved
I would personally look at using a helper method for the test - calling the file ControllerHelper or similar:
#app/helpers/controller_helper.rb
class ControllerHelper
def is_foo? text
# Here the message gets parsed
# i.e if text[0] == "M"
end
end
This will allow you to call the helper in your controller, giving you the ability to use the logic to form the fixes:
#app/controllers/messages_controller.rb
class MessagesController < ApplicationController
include ControllerHelper
def create
model = is_foo?(params[:text]) ? "foo" : "bar"
model.constantize.send(:create)
end
end
I wouldn't call it a ControllerHelper module as mentioned in Rich Pecks answer (since helpers in Rails are view-related), but something like
# app/lib/foo_bar_creator.rb
FooBarCreator = Struct.new(:params) do
def create
build.save
end
def build
klass.new
end
def is_foo?
params[:text] == 'foo'
end
def klass
is_foo? ? Foo : Bar
end
end
(some call these kind of classes "Service Objects")
This way I could just call FooBarCreator.new(params).create in my controller.
Related
I have a user model
class User
def fname
#fname
end
def fname=(str)
#fname = str
end
def greeting
"Hello #{#fname}"
end
end
But I want to remove the greeting method to somewhere else so that my user model don't include the business logic.
How should I achieved that?
I try to create a module(foo.rb) in lib but its not working. Should I include in User model?
Updated Info:
I updated my code
module UserBusinessEntity
def speak(sound)
return "#{sound} is its sound"
end
def greeting
"#{self.id} Hello, #{self.fname} #{self.lname} you are #{self.age} years old"
end
end
class User < ActiveRecord::Base
include UserBusinessEntity
end
This works if both code in same file.i.e. app/models/User.rb
But I want to move the module UserBusinessEntity code to app/services/
Do I have to add require at User Model. If so I added like require UserBusinessEntity But Its gives uninitialized constant UserBusinessEntity
Just create a module like this:
module Foo
def greeting
"Hello #{self.fname}"
end
end
Then include the module in your User module:
class User
include Foo
# ...
end
Then you can call in a controller or a view
#user = User.new
#user.greeting
I believe you may use greeting to render in views or mailers. So this is a showcase of using presenter. A good article is here.
Basically, defining a presenter will be:
app/presenters/user_presenter.rb
class UserPresenter < DelegateClass(User)
def greeting
"Hello #{fname}"
end
end
There are many ways to define, the above is just basic, check out above article for detail.
Then, you can use it anywhere you want to:
#user = User.first
UserPresenter.new(user).greeting
Or even in a view
example.html.erb
<p><%= UserPresenter.new(user).greeting %><p>
Moreover, people may use concern to implement this, but with me that is not a good practice!
I know this sounds like a ridiculous question but I trying to solve a chalange given by an potential employer. I have a schema and a couple of models with their methods. Almost all the methods have no variables passed in. Meaning none of the methods look like this:
def this_is_my_method(variable)
#stuff
end
or
def this_is_my_method variable
#stuff
end
but there are methods that are clearly working with variables like this:
def build_address
if variable
# do something
end
end
Is there a RoR way that a model method will just know about certain parameters or variables in certain situations?
So if my controller was recieving params that looked like this:
?my_method[begin]=1&my_method[end]=5
would the model method "my_method" know what "begin" and "end" where?
def my_method
if self.begin == self.end
# do something
else
# do something else
end
end
Remember that a model method has access to all the attributes (and other methods) of that model instance.
So (for example) this would be a valid model method.
class User < ActiveRecord::Base
def full_name
[first_name, last_name].join(' ')
end
end
This is taking an attribute user.first_name and an attribute user.last_name and combining them to create a new method user.full_name
EDIT as #Sanket has suggested you can pass values into a model if you make them attribute accessor...
def SomeController < ApplicationController
def some_controller_method
#user = User.find(params[:id])
#user.begin = params[:begin]
#user.end = params[:end]
#user.some_model_method
end
end
def User < ActiveRecord::Base
attr_accessor :begin, :end
def some_model_method
if self.begin == self.end
# do something
else
# do something else
end
end
end
Although to be frank I'd rather just pass the values in as method arguments.
I'm trying to share a session variable in both the controllers, the views and the model.
With the following code, it is working in the controllers and in the views :
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :best_language_id
# Returns the ID of the language to use to display the description.
def best_language_id
#best_language_id ||= session[:best_language_id]
#best_language_id ||= current_project.default_language.id
return #best_language_id
end
end
But I can't call it from the model.
I would like to be able to call best_language_id either in the controllers, views and in one model, to get a fallback of the best_language_id if a translation is not found.
Example in my model (not working) :
class Point < ActiveRecord::Base
# Retuns the attached word in the given language if exists.
# Otherwise, falls back on another translation
def word(preffered_language_id)
word = Word.find(:translation_id => self.translation_id, :language_id => preffered_language_id)
if word.blank?
word = translations.where(:translation_id => self.translation_id, :language_id => best_language_id)
end
return word
end
end
I know that model should not include applicationcontroller method calls, but how is it possible to share my best_language_id accross controllers and model ?
Edit : using i18n is not the question here. Translations are not fixed string but variables in a database.
Thanks for helping !
In your rails app, you have a base module in config/application.rb. It should be named after your application. Let's say its called MyApp. What you could do is define two methods like this:
module MyApp
...
def self.language_id=(value)
#language_id = value
end
def self.language_id
#language_id ||= 'en' # default vaule
end
...
end
Then, in app/controllers/application_controller.rb add a before_filter like this:
before_filter :language
def language
MyApp.language_id = session[:language_id] if session[:language_id]
end
Then, from all over the app, you can access the value via
MyApp.language_id
Needless to say that the approach is not thread safe so don't use it in a threaded environment.
I would suggest you switch the situation around, store the best_language_id in the model as a class accessor, then you can set and get it from your controllers and it will still be available in the models.
class Point < ActiveRecord::Base
cattr_accessor :best_language_id # to store the variable
end
# Persist the content of that variable at the start of every action
class ApplicationController < ActionController::Base
before_filter :set_best_language
def set_best_language
Point.best_language_id = session[:best_language_id]
Point.best_language_id ||= current_project.default_language.id
end
end
# Use the variable in a controller
class SomeOtherController < ActionController::Base
def show
#best_language = Language.find(Point.best_language_id)
...
end
end
# Use the variable in a model
class SomeOtherController < ActiveRecord::Base
def some_method
best_language = Language.find(Point.best_language_id)
...
end
end
I created a module, basically what I want to do is,
in this module, there is a function that will work like before_filter. This function will perform the logic and determine what it should perform. Example
class JobsController < ApplicationController
include Mymodule
authorize_resources
def create
end
def update
end
end
module Mymodule
def authorize_resources
current_controller = params[:controller]
if current_controller == 'jobs'
//some logic
end
end
end
so how I actually can automatically detect the controller name based on where my function located such as jobs, users, and etc. This is something similar to CanCan, but I would like to make my own.
Besides, how can I raise an exception or redirect_to a path if it failed, is that need to extend some rails classes?
def authorize_resources
if current_controller.class == 'jobs'
//logic
end
end
Change your if to:
if(current_controller == JobsController)
If params[:controller] is the class itself, and
if(current_controller.class == JobsController)
If the variable is an instance of JobsController.
I am using rails 3.0.9 and devise for authentication. Now I'm trying to use single table inheritance because I need to use polymorphism, so I have two classes: UserType1 and UserType2, which inherit from User class. I need that Devise instance correctly the current_user depending the type of user.
For example,
class User < ActiveRecord::Base
#devise and other user logic
end
class UserType1 < User
def get_some_attribute
return "Hello, my type is UserType1"
end
end
class UserType2 < User
def get_some_attribute
return "Hello, my type is UserType2"
end
end
In controller
class MyController < ApplicationController
def action
#message = current_user.get_some_attribute #depending the type using polymorphism
render :my_view
end
end
it's exactly what you need : http://blog.jeffsaracco.com/ruby-on-rails-polymorphic-user-model-with-devise-authentication
you need to override the sign in path method in your application controller, hope it help.
You will need to add get_some_attribute method inside User model
Module User < ActiveRecord::Base
#devise and other user logic
def get_some_attribute
#You can put shared logic between the two users type here
end
end
then, to override it in the user sub types, like this:
Module UserType1 < User
def get_some_attribute
super
return "Hello, my type is UserType1"
end
end
Module UserType2 < User
def get_some_attribute
super
return "Hello, my type is UserType2"
end
end
Then, current_user.get_some_attribute will work as you expecting, if you like to read more about overriding methods in Ruby, you can read about it here
I added super as I assumed that you have some shared logic in get_some_attribute, as it will call get_some_attribute in User model, you can remove it if you don't need it.
Good luck!