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")
Related
I am practicing making third-party api calls using the rest-client gem. Right now, my code for making a call to a third-party api is stored in a show method in my search_controller.rb. I'm assuming it would be better to store that logic somewhere other than a controller, in order to follow the "skinny controller" practice of ruby on rails. Where would be a better place to put this type of logic, or is this an ok place to have it?
I would say two different kinds of objects in conjunction.
Clients
These are classes that simply do HTTP calls and touch the application boundary. The reason you want to isolate this in a discrete object is that it allows you mock it in tests. It also keeps the responsibilities clear.
# adapted from the HTTParty readme
class StackExchangeClient
include HTTParty
base_uri 'api.stackexchange.com'
def initialize(service, page)
#options = { query: { site: service, page: page } }
end
def questions
self.class.get("/2.2/questions")
end
def users
self.class.get("/2.2/users")
end
end
/lib is a pretty good place to store clients since they rarely should contain any application specific logic.
Service objects
Service objects are Plain Old Ruby Objects (PORO) that are designed to
execute one single action in your domain logic and do it well.
-- Rails Service Objects: A Comprehensive Guide
class QuestionImporterService
def initialize(service, page, client: nil)
#client = client || StackExchangeClient.new(service, page)
end
def call
questions = #client.get_questions
questions.map do |attributes|
Question.new(attributes)
end
end
end
This example uses constructor injection to allow you to mock out the HTTP calls for testing.
You can place services in /app/services. Sometimes Spring gets "stuck" and does not pickup new folders in /app for autoloading in which case you restart it by calling $ spring stop.
I am trying to forward an action to another controller through the service object methodology in rails 5.2.
The create action should pass the user id for the create action but I am failing at passing that param appropriately.
Business logic is the following: a user rents out an asset, the potential renter makes a request, when the asset owner agrees to a visit, the rent user is created as a client in another controller to organise a visit.
I am trying to address the create action in the client controller as follows:
In the rent controller :
private
def visit(room, rent)
#newclient = NewclientService.create(params)
if #newclient.save
rent.Approved!
...
else
rent.Declined!
...
end
and then in the app/service/newclient_service.rb
module NewclientService
class << self
def create(params)
#rent = Rent.find_by(id: params[:id])
user = #rent.user_id
name = #rent.user.fullname
email = #rent.user.email
Client.create(user_id: user, name: name, email: email)
end
end
end
This code does the job. The db is filled up, validations and strong params seem to work and it seems to me robust/secure enough.
Question: is the service object (my way ?) route the most preferred way for forwarding that action ?
Thanks for your help,
I like the pattern in principle and it has really cleaned up the apps which I produce. There are a couple of nice gems that I typically use to get the job done and keep the controllers clean.
I use the mutations gem and simple_command. These two together give you a nice (almost completely) consistent API. The mutations gem in particular is what I use for digesting and resolving JSON input data from params which can then handle processes for me.
This is a good pattern in the sense that it encapsulates the logic of discrete functionality very well. For example, if you have a RegisterUser mutation, you can use that in a controller or you can use it to digest a whole list of objects etc. You can even use the builder option for attributes to process deeply nested json.
I would recommend checking it out.
https://github.com/cypriss/mutations
For those times where I am not processing JSON from an API and want to create discrete encapsulated functionality I generally use simple_command https://github.com/nebulab/simple_command. This approach is also great because it allows you to use the same components from any context. For example, a command called GetLatestTweets.call() could be used equally well from a controller as it could from the REPL.
Both of these libraries provide you with a result object which you can then process as appropriate
def create
outcome = NewClientMutation.run(params.require(:resource).permit!)
if outcome.success?
render json: outcome.result, status: :created
else
render json: {resource: outcome.result, errors: outcome.errors}, status: :unprocessable_entity
end
end
In my particular case I use 'permit!' since the mutations library ignores any parameters that aren't explicitly specified which means that strong parameters aren't necessary if you use this library as it filters parameters as well.
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.
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.
I've put all of my user-authentication code in one place, namely lib/auth.rb. It looks like this:
lib/auth.rb
module Admin
def do_i_have_permission_to?(permission)
# Code to check all of this goes here
end
end
I include this module as part of the application helper, so these functions are available in all the views:
application_helper.rb
require 'auth'
module ApplicationHelper
include Admin
# other stuff here
end
And I also include it as part of the application controller, so the controllers likewise can call the functions:
application.rb
require 'auth'
class ApplicationController < ActionController::Base
include Admin
end
So far, so good.
The problem is that my application is not like a normal web app. Specifically, more than one user can be logged into the system from the same computer at the same time (using the same browser). I do authentication for actions by looking at all the people who are logged in from that IP and if they can all do it, it passes.
What this means is that, if an admin wants to do something, that admin has to log everyone else out first, which is annoying. But we want the admin seal of approval on everything the admin does. So the suggestion given to me was to have it so the admin can supply a username/password combo on any page they would not normally have access to (e.g. an 'edit user' page would have these extra input fields) and the authentication routines would check for that. This means
Admin::do_i_have_permission_to?(permission)
needs to get at the current request parameters. I can't just use params[:foo] like I would in a controller, because params isn't defined; similarly request.parameters[:foo] will also not work. My searching has revealed:
The current search parameters are in the current request,
The current request is in the current controller,
The current controller is in the current dispatcher, and
I'm not sure the current dispatcher is kept anywhere.
That said, experience tells me that when I'm jumping through this many hoops, I'm very probably Doing It Wrong. So what is the right way to do it? Options I've considered are:
Just move all the functions currently in auth.rb into the ApplicationHelper where (I think) they'll have access to the request and such. Works, but clutters the hell out of the helper.
Move all the functions somewhere else they'll see those methods (I don't know where)
I'm just plain missing something.
In a typical Rails application, authentication information is stored in the active session, not the parameters. As such, it's pretty straightforward to write a helper that does what you want.
It seems rather unorthodox to create a module that is then included in ApplicationHelper. The traditional approach is to create a separate helper which in this case would probably be called AuthenticationHelper. This can then be included in any required controllers, or if you prefer, loaded into ApplicationController to make it available universally.
In general terms, Helpers should not include other Helpers. It is better to simply load multiple helpers into a given Controller.
Helper methods have full access to any instance variables declared within the controller context they are operating from. To be specific, these are instance variables only (#name) and not local variables (name). Helper methods are executed for a particular view as well.
Further, I'm not sure why a user would be providing credentials and performing an operation in the same step, at least for traditional web-based apps. Usually the process is to log in and then perform an action separately.
However, in the case of an API where each transaction is an independent operation, the most straightforward approach is to do is pull out the relevant request parameters that deal with authentication, establish some controller instance variables, and then proceed to perform the particular request given the constraints that the credentials impose.
The approach I usually follow for this sort of thing is to layer in an authentication structure in the ApplicationController itself which can perform the required checks. These are protected methods.
While it's tempting to roll in a whole heap of them such as can_edit_user? and can_create_group? these very quickly get out of hand. It is a simpler design to put in a hook for a general-purpose can_perform? or has_authority_to? method that is passed an operation and any required parameters.
For example, a very rough implementation:
class ApplicationController < ActionController::Base
protected
def has_authority_to?(operation, conditions = { })
AuthenticationCheck.send(operation, conditions)
rescue
false
end
end
module AuthenticationCheck
def self.edit_user?(conditions)
session_user == conditions[:user]
end
end
class UserController
# ...
def edit
#user = User.find(params[:id])
unless (has_authority_to?(:edit_user, :user => #user))
render(:partial => 'common/access_denied', :status => :forbidden)
end
rescue ActiveRecord::RecordNotFound
render(:partial => 'users/not_found')
end
end
Obviously you'd want to roll a lot of the authority checks into before_filter blocks to avoid repetition and to promote consistency.
A full framework example might be of more help, such as the Wristband user authentication system:
http://github.com/theworkinggroup/wristband/tree/master