Lets say I have a PeopleController which my users can access when they login to my app
class PeopleController < ApplicationController
def create
# stuff here
end
end
And then my boss tells me we need an API, so we go with something like this in addition to what we already have:
class API::V1::PeopleController < ApplicationController
def create
# stuff here
end
end
Is it unusual to have code duplication like this? Should I be looking for a way to DRY this up? I don't mind a bit of duplication but it looks like I'm going to have to make 99% of our existing codebase available through the API.
What your boss is asking of you is to implement versioning. Versioning is quite useful particularly to ensure backward compatibility of API endpoints.
In such situations, the duplication of code might turn out to be a necessary evil since you wouldn't want updated code in later versions that alter functionality to cause issues in earlier versions.
There are gems such as Versionist that help you out with the process of versioning so that much of the process of duplicating the code and adding the required namespacing is done automatically.
The "Rails way" is one controller, which knows how to respond to JSON and HTML. This is why you have respond_to/respond_with/etc.
There's no reason to spin off a second API controller unless you actually want to have your API and non-API controllers diverge.
If you simply want to route /api/v1/people to the same place as /people, that's a job for your config/routes.rb. If you want to add/change behavior in the API on top of the regular controller's behavior, then you can inherit your API controller from your non-API controller:
class API::V1::PeopleController < ::PeopleController
If its basic crud, you can have the same controller respond to html(for your website) and xml or json format(for your api)
class PeopleController < ApplicationController
def create
respond_to do |format|
format.xml {render :xml => #people}
format.html {redirect_to people_path(#people)}
end
end
end
You can adjust your route based on the format if you want your routes to look different for api
If you API might change over the lifetime the application and requires versioning then you need two different controllers.
However, if you API is maybe for the mobile application do not have multiple users and wont require frequent re-visioning then have a simple controllers and simply user respond_with and respond_to.
Related
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
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.
I'm writing a rails application with an AngularJS front-end, this is part of a tutorial series I'm writing on connecting rails and angularjs. This means my rails application communicates with the browser exclusively in JSON.
In the angularjs $http documentation it describes a potential json security vulnerability where the json request can be embedded into a script tag, plus some tricky use of jsonp, to allow something akin to a cross-site scripting attack. I've found a few other pages, one in particular I thought described this well, and dates from 2008, so this isn't a new issue.
Apparently this isn't a vulnerability in standard rails json rendering, as rails by default provides back an object containing an array. But when working with angularjs we appear to set root: false (although I have to confess I can't find where I did that, but it's definitely not giving the root node).
Anyway, the bottom line is that the angular documentation recommends prefixing any json response with )]}', so:
['one','two']
Becomes
)]}',
['one','two']
Angular then automatically strips that off again.
I'm looking for a way to do this elegantly. I've seen a lot of questions and answers on stackoverflow about this, but most of those either relate to much earlier versions of rails before JSON handling was more thoroughly embedded, or seem to require me to create a lot of boilerplate code. I'm looking for a method that I can apply to the application controller, or as a helper method, that will work everywhere.
The controller that I'm currently using looks as follows:
class ClubsController < ApplicationController
respond_to :json
# GET /clubs.json
def index
#clubs = Club.all
render json: #clubs
end
end
This doesn't call any templates - the render action skips the templating engine. I can get this working by changing the render line instead to:
respond_with json: #clubs
And creating a template file views/clubs/index.json.erb that contains
)]}',
<%= raw(#clubs.to_json) %>
But I'd then have to create a template for every action on every controller, which feels like boilerplate. I'd like instead to be able to change views/layouts/application.json.erb to have something like:
)]}',
<%= yield %>
But that doesn't work because we only get templating if we call respond_with. And if we call respond_with, we have no way to put the #clubs into the response - so we end up with:
)]}',
As the entirety of the response.
An alternative would perhaps be to override the as_json method to prepend what I want, but that seems a bit like a sledgehammer. Ideally there would be a place I could introduce a helper method, something like:
render prepend_vulnerability_protection(json: #clubs)
So, after all that, two questions:
Is this even a real problem, or does Rails already have some other protection that means I don't need to worry about this at all
Is there a way to do this centrally, or do I need to bite the bullet and create all the boilerplate templates? I can modify the scaffold generators to do it, so it's not the end of the world, but it does seem like a lot of boilerplate code
So, no responses as yet. I'm going to write down what I find from my research, and my current answer.
Firstly, I think this is a genuine vulnerability in rails. Unfortunately the rails and JSON/JSONP area has had some other recent vulnerabilities relating to the JSON parser at the Rails end. That has really drowned out any google search relating to this specific XSS issue.
There are a couple of approaches to resolving this:
Have your application only respond to put/post/delete requests. That's not really an option when integrating to Angular - well, it is, but it means overriding a bunch of standard behaviour
Insert something at the front of your returned JSON - this can be the root node (default rails behaviour in rails 3, no longer in 3.1), a closure like )]};, or a loop like while (1);. Angular expects and can deal with )]}',
I've looked at using a json template in my rails app. You can do this with one of many gems, the one I like the look of is JBuilder (railscast 320), but RABL is perhaps more powerful (railscast 322).
This does mean a template for each of the actions on each of the controllers. However, I've also just completed working out how to have rails scaffold those for me automatically, so it's not as scary as it was when I first asked the question, and I can see some other reasons that I might want more control over the json that is returned from my application.
Having said that, I couldn't immediately see a way to get JBuilder to prepend an arbitrary string - it seems to only want to prepare valid JSON (and this I think is not valid JSON). RABL looks like it can do it, but it is a bit more complex. It can definitely be done through just using ERB, but I feel kinda wrong in doing that.
The other alternative I've identified is a helper method in application_controller.rb, which I then call in each of my controller methods. This is reasonably elegant, and I can quite easily change my template to do it. So I'm going with this for now:
class ApplicationController < ActionController::Base
def render_with_protection(json_content, parameters = {})
render parameters.merge(content_type: 'application/json', text: ")]}',\n" + json_content)
end
end
class ClubsController < ApplicationController
respond_to :json
# GET /clubs.json
def index
#clubs = Club.all
render_with_protection #clubs.to_json
end
# GET /clubs/1.json
def show
#club = Club.find(params[:id])
render_with_protection #club.to_json
end
# POST /clubs.json
def create
#club = Club.new(params[:club])
if #club.save
render_with_protection #club.to_json, {status: :created, location: #club}
else
render_with_protection #club.errors.to_json, {status: :unprocessable_entity}
end
end
end
Note that you should be also including CSRF protection in your application controller - so see this as additive to the security precautions you were already taking, not a replacement.
I'm using MongoDB as a backend for my project, but I don't specifically want a dependency on Mongo for the life of the project in case I later decide to change it or in case unit testing is easier without the database. As written, however, my controller is strictly dependent on MongoDB.
Mostly taken from the MongoDB tutorial, I have the following in a config/initializers/mongo.rb file.
MongoMapper.connection = Mongo::Connection.new('localhost')
MongoMapper.database = 'database'
if defined?(PhusionPassenger)
PhusionPassenger.on_event(:starting_worker_process) do |forked|
MongoMapper.connection.connect if forked
end
end
In a controller for querying the US states, I have the following code:
class StateController < ApplicationController
def index
states = MongoMapper.connection.db('database').collection('state').find()
render :json => states
end
end
There are a few issues I see right off the bat (there are probably more):
As previously mentioned, the controller has a hard dependency on MongoDB.
I'm not using the database property of the MongoMapper class (it's hardcoded in the controller).
I don't necessarily want to go through an HTTP endpoint every single time I want a reference to a collection of states --though I'd like to keep this option available. For example, if a signup page has a drop down list for the user to select their home state, it seems foolish to require a client-side jQuery.get() to populate the list of states. It seems to me it would make more sense to have the states pre-fetched server-side and build the drop down list on the server.
For #3, I could certainly query the states in any action method that renders a view that requires the list of states and save them in #states (or something similar), but this would be a lot of duplicate code.
What would be the best way to architect this to allow for less coupling and more code reuse?
First, you should have a Model for states:
class State
include MongoMapper::Document
end
Then, in your controller, you should access via that:
class StatesController < ApplicationController
def index
render :json => State.all
end
end
This way, your controller has no idea what underlying datastore it's using.
Finally, to reduce the need to make a HTTP call, but assuming you're building this in javascript, you code:
<div id="#states" data-states="<%= #states.to_json %>"></div>
Then load that from $("#states").data("states")
I am currentlly trying to design a RESTful MembershipsController. The controller action update is used only for promoting, banning, approving,... members. To invoke the update action the URL must contain a Parameter called type with the appropriate value.
I am not too sure if that is really RESTful design. Should I rather introduce sepearate actions for promoting,... members?
class MembershipsController < ApplicationController
def update
#membership= Membership.find params[:id]
if Membership.aasm_events.keys.include?(params[:type].to_sym) #[:ban, :promote,...]
#membership.send("#{params[:type]}!")
render :partial => 'update_membership'
end
end
end
Neither. The only "actions" a controller should handle are GET, PUT, POST, DELETE (+other http verbs). I realize posting this on a question tagged with "rails" is heresy but today I don't care.
One RESTful way to do this is to create new "processing resources" for each of these operations and POST the member to that resource to invoke the action.
When I say create a new resource, you can interpret that to mean, create a new controller.
To me this is one of the cases when you just shouldn't pull your hair out in efforts to adhere to REST conventions. Your model doesn't seem to fit in with the traditional CRUD concept, and the RESTful principle of differentiating actions via HTTP verbs doesn't seem to belong here too.
If I were you, I would split that action into separate actions for what you need to do with your memberships (trying to stay as DRY as possible). That would make the controller code more readable. And by creating some routes I would also make the view code cleaner (promote_membership_path, etc.). But that's just me :), so see what fits most for you.
EDIT:
here is an article which explains my point of view a bit: http://www.therailsway.com/2009/6/22/taking-things-too-far-rest
Well, there is more than one way to do things. The questions you should be asking yourself, is how many states are there? How often do new states get added? Etc.
If there wouldn't be that many states, I would create separate actions + a before filter for the find, but I think this is more of a personal preference. If you really want to keep it short, you can put each method on one line. So:
class MembershipsController < ApplicationController
before_filter :find_membership
def ban; #membership.ban!; render :partial => 'update_membership' end
def promote; #membership.promote!; render :partial => 'update_membership' end
protected
def find_membership
#membership = Membership.find(params[:id)
end
end
To answer your question whether it is RESTful: yes, your update method is perfectly RESTful, but remember that a PUT should be idempotent. So if I execute the same method twice, is the result the same? i.e. what happens if I ban a user and ban him again?