Before I begin, this is basically a port of an existing ASP.MVC REST style service to Sinatra. I am fairly new to Ruby so dont really know the best practises yet, and the web seems a bit torn on the subject.
So currently in the ASP flavour we have some controllers like so:
public class MyController : Controller
{
[Authenticate]
public ActionResult AccessSecretStuffs()
{...}
public ActionResult AccessPublicStuffs()
{...}
}
Now we use a filter to make sure that any actions with Authenticate check for authentication from the current user before loading the action. The routes setup for these are fairly vanilla, they just map to the controller and actions.
Now in Ruby land using Sinatra you dont tend to have controllers, everything is an action, and as we are purely looking to expose data in a restful way this seems great. However I need to be able to make sure that whenever certain routes are called with Sinatra that it will basically call a bit of code first to check if they can access the action, then if not redirect them to the login page.
I was thinking of just putting in some AOP to cover this, but after a bit of reading everyone says Ruby doesnt need AOP and already provides most of the functionality out of the box. So can anyone shed some light on the best practise for doing this?
Sinatra documentation has a before method that would be applicable, but then I would need to do a before method for every other route method, which isnt ideal. In my mind I just want to declare a route, and then put some sort of annotation/attribute at the end which indicates that something needs to happen...
Hope that makes some sort of sense :)
Ruby doesn't have annotations as such, but Sinatra does provide custom filtering mechanisms.
When defining a route, you can put additional conditions, such as various header values and whatnot. You can also define custom conditions:
set(:auth) do |*roles| # <- notice the splat here
condition do
unless logged_in? && roles.any? {|role| current_user.in_role? role }
redirect "/login/", 303
end
end
end
Then you can define a route like this:
get "/secret", :auth => [:user, :admin] do
...
end
or if you don't need to pass any arguments, before block can take a path as an argument:
before '/secret/*' do
authenticate!
end
You can read more at Sinatra readme page
Related
I've been looking around for best practices on where to put some helper methods that do all this calculation / sorting for me but I haven't found a definitive answer yet and was wondering if someone had some good insight for me.
Basically I have an action method that takes a string user input, finds similar words to that string, does a bunch of string manipulation, and then ordering to return an array.
I don't know whether I should have module in /lib, make a controller helper module, ... Where I'm looking for some feedback!
But essentially I just want to:
POST a word to a controller action method
Call a helper method or execute some logic on the word outside of the controller
Have that helper method or wherever that logic will be, return to me the result
Just put that method in your application helper, wherever it is called from it will return the resulting values after processing your logic, you should not over complicate things so the good old application helper is a good place to put the common method used in views and in your controllers
I wouldn't call a helper method from your controller. Personally I would use either a module/plain old ruby object or a concern to handle calculations. The controller stores the object in an instance variable so it can be used througout your views.
Also worth noting are decorators, you should take a look at draper: https://github.com/drapergem/draper
So, I've been able to get restful controllers working with
Route::controller('users','UserController');
class UserController extends BaseController {
public function getAccount(){}
}
and so /users/account works. However if I try to do something like
Route::any('account',array('as' => 'account','uses' => 'UserController#account'));
and go to /account, it doesn't work (NotFoundHTTPException). Is there a way to use named routes and restful controllers in conjunction? I like how the restful system breaks up requests, and how named routes encapsulate the URI's and decouple them from the function names. This worked in Laravel 3. Am I missing something in the syntax, or did Laravel 4 purposefully disallow this kind of mix-and-match behavior? Thanks...
This would depend entirely on the order you have defined the routes. If it's not working try reversing the order of the definitions.
But because Laravel is all about making your life easier you can pass an array of method names and their corresponding route name as the third parameter to Route::controller.
Route::controller('users', 'UsersController', ['getProfile' => 'user.profile']);
This might not directly apply to your situation but it is super handy.
Try this:
Route::get('/',array('as'=>'named_route','uses'=>'yourRestfulController#getMethod'));
This works nice for me. The trick was adding the action type after # part. You should use the full name of the method unlike in L3.
This works nice for me. The trick was adding the action type after # part. You should use the full name of the method unlike in L3.
Because REST prefix get, post, and so on are patterns to distinguish what type of REST it implements. When you named restful controllers route they didn't act like RESTful controllers anymore but a normal Controller you wish to named. Example of this:
Route::get('user/profile/', array('as'=>'dashboard', 'uses'=>'ProfileController#showDashboard'));
Consider this one:
Assuming we want SystemController to be a RESTful controller so you'll define:
Route::controller('/', 'SystemController');
Then you want to named the postDashboard on the SystemController as dashboard, so you'll modified your routes as:
Route::get('user/profile/', array('as'=>'dashboard','uses'=>'SystemController#postDashboard'));
Route::controller('/', 'SystemController');
On that scenario,postDashboard should not be access via GET protocol since we declared it to bePOST, that is if Laravel treated it as RESTful Controller, since we named it that way it will be treated as normal not RESTful, so we can access it tru GET protocol. Naming it that way will be so dramatically not appropriate, coz we are breaking what we want first which is telling Laravel to treat SystemController as a RESTful.
I think you have to consider the post of Jason Lewis as the appropriate answer. No hard feelings #arda, since you are also correct.
I have been working on implementing my own ORM. And I was wondering how the rails path helper extracts the ID from the object. For example how would I make this work for my ORM?
#contact = Contact.first
contact_path(#contact)
Any help would be greatly appreciated!
Update:
My object does have an ID attribute and responds to it. But yet the path helper returns an error.
In a nutshell you want to be activemodel compliant. This will make url helpers, form_for(#contact) and so on work.
You also get to (optionally) use a bunch of modules dealing with
things such as validations, dirty attributes etc.
There are only a handful of methods you have to implement. There's also an
ActiveModel::Lint module that tests that your implementations of these
primitives are valid, and which also serves as documentation. In particular you need to implement to_param and persisted?. I think some of the naming stuff only gets used if you do stuff like link_to 'foo', #contact
The method checks to see if you've passed it an object, or an integer. If it's an object and that object has an id method (respond_to?(:id)), it uses its ID. Pretty dead simple.
I'm building an application that consumes models through an api with ActiveResource. I noticed that the
#resource ||= #resource.do a query
doesn't work, i.e. If I put something like that in my controller, my application will still query the api. So there is no built in caching that I'm used to with ActiveRecord. Time to expand my knowledge and skill base, ok.
I found this: http://injectisforwizards.com/blog/read-through-caching-of-activeresource/, and while I don't understand this 100% yet, for controller based queries that do .find, this appears to work. But not for any custom queries I have e.g.:
#current_resource ||= Resource.get(:resource_all, :by_account=>#current_account.account_key)
(which hits a custom controller and runs a scope, returning a collection)
I'm working through this and I'll find out what is going, but I'm curious if someone could tell me simpler terms what is going on, what I can do to smooth over caching in ActiveResource to be more like ActiveRecord, how I can tailor this to cache all queries, etc. Anything really would be helpful.
EDIT:
I found this: https://github.com/Ahsizara/cached_resource which looks promising but it is new (and built off that link above)....notable is that it does not seem to handle any sort of collections, but for one resource finds/caches well.
Since you receive a new instance of a controller for each request, caching like that is not going to work. I'm presuming you have something like this:
def show
#resource ||= expensive_request
end
What are you expecting #resource to be when that method executes? The result of something from a previous call to show? Not going to happen.
What you need to do is put things into Rails.cache if you want them to persist between requests. If you use memcached as a back end, this can be very efficient. See the Caching with Rails guide for more information around section 2.5.
In general, a better approach than the ||= method of caching is to define protected methods that are wrapped with memoize to save the results. For example:
def load_resource
expensive_request
end
memoize :load_resource
Remember that this will only apply to subsequent calls to that method within the context of the same request and is not the same as using Rails.cache.
I feel like the biggest idiot - I thought only ActionResults were sent back to the client. For this reason, I had a load of "helper" methods in my controller.
I just tried accessing them via a URL and I swear I almost went crazy when I saw they can be reached! ... Luckily, no one else has discovered this yet.
One such method I have, that I call over and over again is :
public User GetCurrentUser()
{
User user = db.Users.SingleOrDefault(x => x.UserName == User.Identity.Name);
return user;
}
I have just created a folder called "Logic" inside my Models folder and I am attempting to separate the code - I was just wondering what the best strategy is for calling code, namespaces and more?
In the above example, I am using User.Identity.Name which only inherits from Controller. If I add this, I am back to stage one!
Thanks to Darin Dimitrov, I now know about the [NonAction] Attribute - which, adding to these methods does exactly what I need/fixes the security problem, however, many of these Methods are used in different controllers and I know it would be best if I can separate the logic from the controllers. I am just not sure what the best way to go about it is.
Can anyone point me in the right direction before I start pulling all my hair out!?
You may take a look at the [Authorize] attribute.
UPDATE:
Or you could use the [NonAction] attribute or make the method private. But the best practice in this case would be to simply move this logic out of your controller. A controller should contain only controller actions. Other logic should be placed in its respective layers.