ROR creating standalone components - ruby-on-rails

In my web application I have a model User. It's quite common that you need to select some users for many different purposes related to many different models. My aim is to make this component very easy and fast to attach in a new place. E.g. if a users wants to select his friends the result of selection should be handled by User controller, but if you want to assign some users to a task this should be handled by Task controller.
Do you have any concept how to do this? Should I make another controller for selecting? How should I pass the selection to the suitable controller? Maybe by session? Do you have any other suggestions?

I think what you are looking for is a module which has common methods. If so you can do something like:
Create a module called Users and add the methods to that, and keep it inside your lib folder
Ex:
<app root>/lib
module User
def friends
<returns the given users friends>
end
end
and then you can call this module in both your controllers and models
Ex:
Class Friend
include User
end
Class FriendsController < ApplicationController
include User
end

Related

How to write and include regular ruby classes in rails

I'm learning Rails. I have a controller responsible for presenting data from parsing files uploaded by the user. I don't want the data to be stored anywhere in the model. Can I include a class that I can instantiate in my controller method? Here is a basic code example of what I mean:
This controller only contains one method:
class MyController < ApplicationController
def index
test = FileProcessorService.new
#test = test.test()
end
end
Here is the class that will handle the logic when the instantiates calls its method:
class FileProcessorService
def test
return 'This is a test'
end
end
My questions:
Where is the best place to store this class? How can I refer to this class in my controller?
Any advice on this particular topic of using classes in rails? Are instances of a regular ruby class a problem in the controller? I dont want my users seeing the same data. Thats why I dont want to include global variables in my controller. No models since I have an MVC back ground with Java MVC. I'll move on to models once I understand how the basic rails controller functionality works.
Thank you in advance for your help.
I usually put these in app/classes, or if there are a lot of them, into more specific folders likes app/services, app/notifiers, etc.
You can enable autoloading in config/application.rb:
config.autoload_paths += %W(#{config.root}/app/classes #{config.root}/app/services)
If they're not application-specific, extract them to a gem.

How can I insert this subdomain filtering in an existing Rails application?

I have an existing Rails 3.2 application that is basically a simple product catalog. Now I am faced with a situation where I need to only allow interactions with specific products based on the subdomain the user is using to access the site.
The Product model belongs_to a Repository, which has the subdomain as a value.
So that a request to:
http://bobs_store.myapp.com/products
only shows products that have a repository with the name bobs_store.
Where is/are the best place/places to introduce this filtering? Is there some kind of default scope I can create at the model level to do this? My only problem with that is that I can't access the request from the model, so does this mean that I have to pass the subdomain in to every call I make to that model?
One other thing, all of the calls to the Product model are made like this:
current_user.products.<whatever>
Is it possible to modify my current_user helper method somehow to get my desired functionality? Barring that, is there something fancy I can do with routes? These are shots in the dark, but I'm hoping there is some Rails shortcut I can use that I'm not aware of.
As luri G mentioned, watch this Railscast:
221-Subdomains
Create a scope to query products of a repository
class Product < ActiveRecord::Base
belongs_to :repository
scope :for_repository,
(lambda do |repository_name|
includes(:repository).
merge(Repository.where(name: repository_name))
end)
end
After you had implemented the subdomain route handling logic as per the Railscasts, in your controller you will call the products for a repository like this:
current_user.products.for_repository(request.subdomain)
The way I've seen it done before is to have a scope on the model, e.g. Product.repository_scope.
class Product < ActiveRecord::Base
def self.repository_scope(repository)
where(repository: repository)
end
end
I'm not sure about a good workaround for not having to pass in the current repository every time.

Where should I place a "notify user" method in a Rails app (or any MVC app)?

I have heard several views on MVC structure in a web application. Some people believe the model should be very small, and only contain ActiveRecord (or some other ORM) and small things like validates and belongs_to, so model's are only a couple lines long.
I personally think the model can play a larger role in apps. For example, I need to notify a user frequently throughout my app. The notifications can trigger from multiple controllers. They all send a notification to a user based on his or her notification settings, which is an object accessible through the user model.
I would like to make something like:
class User < ActiveRecord::Base
#validations, relationships, etc
def notify(event)
notif = Notification.new
notif.type = event.type
notif.to = self.id
# etc
if notif.save
# send the notification based on the settings
end
end
end
This would make it possible from any controller to use #user.notify to deliver messages to a user. This makes things really clean, but I realize there is some logic in my model. Not to mention the model makes another model.
Another approach I wouldn't mind is creating the notification and sending it through that object. So controllers could do Notification.new(:to => #user.id, ...) and then deliver the message via a Notification.send. This would also put some logic in the Notification object so it knows how to send itself. In fact, I might like this approach better than creating the notification object through the User model.
I don't mind these small bits of logic in the Model to make things convenient when sending messages from multiple controllers. Is this the best way to go about it? I suppose a more purist method is to include a NotificationHelper module in each controller that sends notifications?
edit: I've been reading a bit about "domain logic" in models. It seems like this is encouraged, and I suppose the notify or send methods are considered domain logic. Can any developer experienced in MVC comment on this?
For your second approach you can use ActiveRecord::Observer
class NotificationObserver < ActiveRecord::Observer
def after_create(notification)
# some sending logic
end
end
But don't be shy extract small pieces of logic into their own classes, like
lib/user_notify.rb
class UserNotify
def event_trigger(user,event)
# some logic
end
end
use naming concepts suitable for your system (maybe Notify.send_user_about_event, etc.)

Extending model class

I'm creating a authentication engine. It has a built in model User, which contains some basic information. I want to make it possible to extend it from outside the engine (from parent app). For example to add some has_many relations. I almost managed to do this by creating:
#host_app/app/models/my_user.rb
calss MyUser < User
has_many :comments
I thought it was it, but another problem turned out. In my engine in users show action's view I generate an additional partial which is supposed to be in host_app/app/views/shared/_partial_to_add.html.erb I also pass there the #user. This should enable me to add some additional information about the user (like the list of comment's he wrote or whatever) without touching the engine itself. But it it turns out (which is quite obvious) that this #user is almost useless because it doesn't have any new methods from the MyUser class.
Any ideas how to fix this?
Ruby has open classes. If you want to extend your engine's User model, you should be able to do this from the parent app:
User.class_eval do
has_many :comments
end
(This uses class_eval so that Rails will autoload the file defining User before evaluating this code.) Put that in a file which will load when the application does; either put it in config/initializers/, or put it lib/ and require it.

RoR : Polymorphic Controllers

I have an existing site that has a bunch of different models and controllers. I am currently integrating Twilio's services into this site. Twilio allows you to supply a url that will be called when a user interacts with your phone number using their phone. Unfortunately, there is only one url that you can provide to Twilio and then all the parsing is done on your end.
So, now I have a twilio controller which parses the user's data and decides what they are trying to do.
Everything the user may be trying to do via their phone can be done on the website already, but now they have the option to use their phone when on the go. If they text my number "create group foo" then the site will try to create the group accordingly. My issue is that I already have a groups controller that knows how to create groups and has the appropriate before_filters to make sure that the user has permission to do so, amongst other things.
Is there a way for the twilio controller to parse the request and then "forward" it over to the proper controller in some way? I'd rather not have the twilio controller duplicate all of the code and filters that are in every other controller and some of that stuff doesn't feel right to be shoved into the models.
I'm somewhat new to rails in general, so I'm open to any suggestion. I'm hoping there's some design pattern out there that fits my use case and I'm willing to refactor my whole project for the correct solution.
I think there are a couple of things you can do. If you don't have to respond in a certain format, then you can simply redirect the request with the appropriately formatted parameters. For example:
class TwilioController
def create
if params[:twilio_action] == 'create group'
redirect_to create_group_path(:id => params[:group_id], :number => params[:number])
end
end
end
There's a good chance that you'll have problems with authentication though, because the twilio api will not be sending and receiving cookies for you, so you will not have an authenticated user. If this is the case it will be best to put all your shared code in the model and handle cookie authentication with your GroupsController and phone number authentication with your TwilioController. For example:
class TwilioController
def create
if params[:twilio_action] == 'create group'
if can_create_group?(params[:phone_number])
Group.create(:id => params[:group_id])
end
end
end
end
It's always best to put your business logic in your model, but if you do actually have a function you want to share within two controllers you can always create a module to do that as well:
module GroupControllerActions
def create_group user
Group.create(params[:group].merge({:user => user}))
end
end
class TwilioController
include GroupControllerActions
def create
if params[:twilio_action] == 'create group'
create_group(User.find_by_number(params[:phone_number]))
end
end
end
class GroupsController
def create
create_group(current_user)
end
end

Resources