Using multiple controllers in one view in Rails - ruby-on-rails

I'm working on something like a social networking mesh; I am using different API's from various websites, e.g. Last.FM, Delicious, Twitter, ...
I've created one controller per each website (currently, there are 7).
Sample views:
localhost:3000/lastfm <- All datas i gathered from user's Last.fm account
localhost:3000/twitter <- All datas i gathered from user's Twitter account
...
Now I want to show these datas in one view (localhost:3000/index.hmtl) by using these different controllers.
Components are deprecated, creating one controller and bury all the API's in that seems ugly, too..
So I don't know how to do this. Any idea?

First off, you should put all of the data-storing and data-gathering methods into Resources and Models so they are accessible from all controllers. You can keep the internal data-mutating operations in your individual controllers though. Once you have it organized like this, you could do what Hobo does: create a controller just for the front page, "front_controller" if you will. Here you can display data gathered from all of your models and resources, as well as links to your other controller actions.
These are a few interesting thoughts on better organizing your models and controllers (fat models, skinny controllers is a rule of thumb.
Since you said you are using other API's (like lastfm and twitter), you might want to take a look at this railscasts about creating non ActiveRecord models (models that are not tied to a database)
here is some pseudo code, keep in mind its really only targeted at your question.
# pseudo code
class TwitterController < ApplicationController
def index
#services = {
:twitter => TwitterModel.find(:all, ...),
}
end
def update_twitter
TwitterUpdaterClass.update { |twit|
_m = TwitterModel.new
_m.message = twit.msg
_m.from = twit.from
# ..
_m.save
}
end
end
class MyIndexController < ApplicationController
def index
#services = {
:twitter => TwitterModel.find(:all, ...),
:lastfm => LastFmModel.find(:all, ...)
}
end
end
it might be much better to have background-workers update your rest-services instead a controller which you need to invoke every time you want to fetch recent tweets.
here is nice article showing - 6 ways to run background jobs in rubyonrails
# more pseudo code
class TwitterWorker < BackgrounDRb::MetaWorker
set_worker_name :twitter_worker
def create(args = nil) # instead of TwitterController.update_twitter
TwitterUpdaterClass.update { |twit|
_m = TwitterModel.new
_m.message = twit.msg
_m.from = twit.from
# ..
_m.save
}
end
end

First off, you should put all of the data-storing and data-gathering methods into Resources and Models so they are accessible from all controllers. You can keep the internal data-mutating operations in your individual controllers though. Once you have it organized like this, you could do what Hobo does: create a controller just for the front page, "front_controller" if you will. Here you can display data gathered from all of your models and resources, as well as links to your other controller actions.

I think you should read a bit about rails' MVC architecture.
It seems to me that you are neglecting the M(odel) part of it. The models should hold the data, thus being the most important part of your application.These are a few interesting thoughts on better organizing your models and controllers (fat models, skinny controllers is a rule of thumb. Since you said you are using other API's (like lastfm and twitter), you might want to take a look at this railscast about creating non ActiveRecord models (models that are not tied to a database)
If you also provide an API for your users, I suggest using a RESTful approach, as it can really be easy to develop and maintain once you get the hang of it. You should read more about resources, as your localhost/lastfm and localhost/twitter are resources and not views.
Hope this helps. Good luck

Related

Rails 4.2 Dry up Controller

I have a User model which gets accessed through a REST API and ActiveAdmin. Since there is a lot duplicated code (almost all) it might be a good idea to dry things up. There are a lot of topics out there handling this issue but i do not find a proper way. I put some of the ideas i have below
UserController instance in each controller
Again duplicated code gets created and the functions
#AA
controller do
def create
#userController = UserController.new
#userController.create(params)
end
end
#REST API
#Create instance through callback
def create
#userController.create(params)
end
Inheritance
I would like to have one basic CRUD controller lying above my model through inheritance like this
Class BaseUserController < ApplicationController
Class Api::UserController < BaseUserController
but (yet) i dont know how to do this in AA also in mind with the inherited_resource gem in mind
Concerns
Also concerns seem to be a nice way. But i would then create seperate concerns for each action to have one concern to exactly handle one behaviour
aspect and also to make them more reuseable . This then again seems to unnecessarily bloat the app and also concerns are not really made to be used like that. (?)
Modules/Mixins
Since modules cannot be instantiated but implement actions fully, this might just be what i am looking for. So each controller includes the desired actions or extend fro ma model rather than a class then.
The thing is - the more i read the more i get confused. I hope some of you guys might help me bring some light or i might turn to the dark side an use C# and .NEt again :D
Thanks in advance!
Example Code
I took this create method for a SMS resource in both, the REST API and the AA, where i call a couple of Services, but if complexity grows i have to add these lines in two places. How could i avoid that? Cant i have a basic SMSController which is used from the API endpoints as well as the AA interface?
controller do
def create
#customer =Customer.find_by_phone(params[:sms_message][:phone])
SmsService.sendSMS(#customer[:phone],Cryptoalgorithm.sms(#customer[:id], #customer[:recharge_token],params[:sms_message][:text].to_i)
#sms = SmsMessage.create(:customer_id => #customer[:id], :text => params[:sms_message][:text], :phone => params[:sms_message][:phone])
#customer.increment!(:recharge_token)
redirect_to admin_sms_message_path(#sms[:id])
end
end

Ruby on Rails - Controller without Views

Iam new in Ruby on Rails. Normally I work with other web languages and now (of course) I try to compare it with other languages using on web.
But sometimes i have some problem to understand the philosophy and the character of Ruby on Rails. Of course i understand the concept of MVC.
But now Iam not absolutely sure:
Is ist OK to create and use a controller without views? In some cases you need a "class" for some usefull functionality used by other controllers they have views. Or is it a better style to use a module?
I try to find it out by reading a lot of articles and examples, but didnt find detailed information about this content.
When developing Ruby On Rails apps, it's recommended to put most of your business logic in the models, so for the controllers the logic need to be minimal to provide info for the views, so if it doesn't work with a view chance that you need a controller are really low.
Is it OK to create and use a controller without views
Yep it's okay.
The key thing to keep in mind is that Ruby/Rails is object orientated.
This means that every single you do with your controllers/models etc should have the "object" you're manipulating at its core.
With this in mind, it means you can have a controller without corresponding views, as sometimes, you just need to manipulate an object and return a simple response (for example with Ajax).
--
We often re-use views for different actions (does that count as not having a view):
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def search
render :index, layout: false
end
end
The notion of skinny controller, fat model is sound in principle, you have to account for the times when you may need small pieces of functionality that can only be handled by a controller:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
#user = User.find params[:id]
#user.update
respond_to do |format|
format.js {render nothing: true}
format.html
end
end
end
A very rudimentary example of Rails is a drive-thru:
view = input interface
controller = accepts order & delivers
model = gets order packaged etc in backend
There are times when the controller may not need a view (for example, if you update your order with specific dietry requirements), and thus the notion that every controller action has to have a view is false.
It's really about making your controller versatile enough to manage your objects correctly.
Shared controller methods can be placed in ApplicationController. Also, in Rails 4 there are concerns (app/controllers/concerns/) where you can put the modules with methods that can be used by multiple controllers.
Controller handles request and renders corresponding view template. So controller without view is absolutely nonsense.
Every time request will execute this controller, it will just end with missing template error, so you will need to create view folder and put empty files with action name inside of it, which is obviously stupid.

Routing for Multiple User Roles

I have a user model that differentiates into 6 roles, and I am defining different variables and directing them to different view files (similar format, but different tables of information), but within the same controller action, because they all have similar pages "overview", "profile", "messages", etc.
Now the controller is really messy, and has multiple if/else statements. I believe I should be changing the routes so that each user has its own controller, eliminating the use of the if/else monstrosity that currently invades the controller.
def index
if current_user.admin?
....
end
if current_user.moderator?
....
end
end
Question: How do I perform the routing such that url will be
www.website.com/1/schedule, where 1 = current_user.id, while having different view files rendered from the different controllers?
I am thinking of doing a AdminController and a ModeratorController to handle this, but am not sure how to do the routing, or if indeed this is the best way to do it. Thanks for advice in advance!
I think you are doing it the wrong way , If you have similar pages for different role then I think you are accessing same model for handling different roles.
If this is the case then you should use Gem like Cancan (authorization library for Ruby on Rails).
To address your second concern, I don't think this would be the best approach. Try this - move your logic away from multiple controllers, and keep the logic in the ApplicationController. I do not think that you should separate the different roles to controllers. Instead, look to your User model and put in a method that checks the privilege level for the different users. You could create methods in your User model that you could call in your controllers to see if the user is allowed access to the action. before_action would be recommended here.
If you decide to keep multiple controllers, I recommend a gem like Authority. Makes it much easier to keep track of different privileges, even across different controllers - I think routing is addressed as well: https://github.com/nathanl/authority

rails 3, is there a way to keep skinny controller when controller uses lots of session info?

I like a nice skinny controller as much as the next guy.
But the app I'm working on talks extensively to a remote system via JSON, and the session variables are used extensively... each little interaction is a separate hit to one of dozens of controller methods, and rails' built-in session tracking keeps track of all the state.
Consequently, the controller has dozens of methods, one for each "interaction" and those methods extensively read (and sometimes write) the session.
It's my understanding that if you're accessing the session in your model methods you're doing something horribly "wrong" ... but having 100+ lines of code PER controller-method for dozens of controller methods seems "wrong" too.
Given that scenario, what IS the best tradeoff between fat controllers vs models that access the session?
I recently came across a similar problem. I was writing and application that depended heavily on a remote API that accepted/returned JSON. Subsequently, I had very few models in my application, and my controllers were quite lengthly.
I ended up writing a custom parser module in lib/ and made calls to the Module (which handled a lot of the logic that would normally exist in the controller)
Consider the following:
module RemoteParser
def get_user(user_id)
result = ActiveSupport::JSON.parse(NetHttp.get('http://www.example.com/'))
session[:received_user] = true
end
end
Now your controllers can be skinny again:
class SomeController < ApplicationController
def index
#user = RemoteParser::get_user(params[:id])
end
end
I'd just like to add that this is an incredibly contrived example of such uses. If you provide additional information about what your requesting, etc, I can help you further.

How to organize actions that don't fit into the normal MVC

I am creating a survey application so I created a surveys controller that behaves very restfully creating, updating, etc a Survey. However now I'm adding other actions to it like 'take', for taking a survey, and 'share', for sharing a survey. There are more actions too. I'm starting to wonder if I should organize my code differently and move those new actions into their own controllers however I'm not so sure take or share or some of my other actions really fit into REST really well. They almost make more sense as actions if I wasn't a little worried about the survey controller size.
Either I could leave it the way it is or I was thinking of creating a survey namespace and creating like Survey::TakeController and the Survey::ShareController. Then I would then I guess use the new action or index?
I'm not exactly sure the proper way to do it. If I do create a survey namespace should I then move the orginal SurveyController in it? That would make some weird looking methods like survey_survey_path.
To think RESTfully, you should probably stop thinking of them as "controllers with actions" and start thinking of them as "objects that can be created/updated etc" - controllers are just proxies for the views that show the results of creating/updating an object.
A lot of the time, I've found that an extra action is really just a variation of "update" - just with its own special requirements (eg only certain people can update it or whatever). That sort of logic can often go inside the model itself (eg "MyModel#can_be_edited_by?(some_user)").
Sometimes you find that actually you have an extra "hidden" model that needs its own RESTful interface.
Eg with your "take" a survey - I'm guessing, but what you have is something like a "SurveyResult" and a person can "create "survey" but when they "take" a survey, they are actually creating a "SurveyResult" (the other commentor called this a "SurveyParticipation" - but it's the same thing).
The thing being that you will probably have multiple SurveyResults that each belong_to :survey and belong_to :some_user_model.
Then you can set up nice restful routes such as:
/surveys/123-my_favourite_colour/results
which will return a set of all the results for a single survey
This is actually the RESTful way to view this part of your object-space.
As to sharing a survey - that's a more intersting question. It depends on how you've got your authorisation setup for "sharing". It also depends on what you mean by "share".
Are you sharing the results of a survey, or sharing the survey-object itself (so another user can edit the questions) or are you (as a person that has just taken part in a survey) sharing a link to the survey so that your friends can also take the survey?
For the first two above - I'd consider a "SurveyPermission" class that belongs_to :survey and belongs_to :some_user_model.
The you can create a SurveyPermission for another user - and Surveys can be edited by the creator- or anybody that has a permission to edit it.
Thus the share action is to create a SurveyPermission.
Though to be honest - your SurveyPermission is likely only to be used for create and delete, so it may be simpler to stick those two actions in the Survey controller.
For the latter - well, that's just sending a "create_survey_result(#survey)" link to somebody...
Update:
I don't generally bother with namespaces unless there are two resources named the same thing (but in different contexts). You only need namespaces to disambiguate between them and that doesn't seem to be the case here.
In this case - the only namespacing that occurs is in the routing:
map.resources :surveys do |s|
s.resources :results
s.resources :shares # ???
end
gives such routes as:
new_survey_path
surveys_path
new_survey_result_path(#survey)
survey_results_path(#survey)
If you want to stay with the RESTful approach then you could have a SurveyParticipation resource, with the new/create actions being invoked when somebody takes a survey. And a, ahem, SurveyShare resource for sharing a survey. That name's pretty awkward though!
Personally I don't think it's the end of the world if you have to augment your existing controllers with a small number of additional actions, as long as it doesn't get out of hand. RESTful Rails saved us from the bad old days when controllers had tens of actions.

Resources