Implementing a multi-parameter search service - ruby-on-rails

I would like to design a search service which applies to a multiple set of different request paramaters. It could be called with one parameter only, or with more.
The problem is that some of the params could only be used solely, or are mutually exclusive with others.
Another problem is that some params are really required, while others are only optional.
The easiest way to resolve this is to involve a series of if-statements in the controller's search method, checking for the existence of some parameters or sets of parameters, and reacting correspondingly. Coming from the world of Java however, I have been taught to disbelieve anything that ends up in a bunch of if or switch statements. Back there, we often resort to factories, to which we delegate the decision to choose a proper search strategy from a bunch of strategy classes. All that was ever necessary, was to configure a proper mapping between request parameters and strategy classes.
I also know that the Rails dev mentality tends to solve about problem in a much less abstract and more business-like manner.
What is the proper way to make complex multi-parameter search service?

Rails 2: searchlogic
Rails 3: meta_search
Then it's as easy as Model.search(params[:search])
Sleep easy.

Related

Adding multiple filters to graphql-ruby implementation

I'd like to add filtering and sorting to my GraphQL types (using graphql-ruby gem).
I've been looking for existing solutions for that and the only reasonable option I've found is creating a custom resolver.
The problem with this approach is that I "lose" Active Record capabilities like using custom methods on the models for filtering or field resolution.
Has anyone come across a decent (and existing) solution for this?
It's worth mentioning graphiti which offers these out-of-the-box but diverts from the GraphQL spec and uses JSON:API instead. It's great but I'd like to benefit from GraphQL subscriptions and built-in authorization.
There are some gotchas with respect to having a generalised solution for sorting and filtering and the GraphQL standard. It's hard to define a concrete schema for a set of arguments that could easily apply to different types well. Because you might naturally have something like sort: Enum[name_asc, name_desc, country_asc, country_desc].
If you are happy with that limitation, and willing to receive your params as some kind of generalised sort: String and write code to ensure what is sent is a valid value. Then you can use a Connection (https://graphql-ruby.org/pagination/using_connections) for this purpose.

Rails, handle two sites with different url and design but with the same db

I'm looking for the best way to solve a problem.
At this moment I have a site for a customer, example.domain.com
My customer ask to create another website with some changes in design, but the contents are the same of the first website. I don't want to duplicate the website, because every feature I add to the site A must be deployed also to site B, and I'm looking a smart way to handle the situation.
I need to keep two different domains and I need also custom mailers and other small tweaks in the controllers (and maybe in some models).
My idea is to put in application controller a before filter like this
before_action :detect_domain
private
def detect_domain
case request.env['HTTP_HOST']
when "example.domain.com"
request.variant = :host1
when "example1.domain.com"
request.variant = :host2
end
end
Then I use the variant with some conditional to choose the mailer, to customize the views and to apply some code changes.
Any other idea?
Using a before filter and a per-request variable like your proposal will work, with a couple caveats that I'll mention below. I'd recommend a tool like the request_store gem to actually store the per-request value of which "skin" is selected.
Now, for the caveats. First, the main problem with per-request variables is that your Rails app does not always exist in the context of a request. Background jobs and console sessions operate outside of the usual request/response flow of your app. You will need to think about what happens when your models or other non-controller/view code is executed when that variable isn't set. I would suggest simply not having your models depend on RequestStore at all -- have the controllers pass any request-specific information down into the models, if needed.
Secondly, it's not clear from your description if you want any data or logical separation between the two domains, or if you just want different look-and-feels. If the former, you might consider the apartment gem, which aims to make database multi-tenancy easier.
EDIT: I also want to mention that, as an alternative to the multi-tenant solution above, you also have the option of a multi-instance solution. Wherein, you use an environment variable to indicate which version of the site should be displayed, and spin up multiple instances of your app (either on the same server with a reverse proxy, or on separate servers with separate DNS entries or a reverse proxy). The downside is increased infrastructure costs, but the context problem I mentioned above no longer exists (everything always has access to environment variables).

Should I prevent making I18n translation calls in models

This is a question merely about code style.
I am working on a multilingual app in Ruby on Rails 4. My personal feeling tells me ideally to not do calls to I18n.t() in models, I just think model methods should be consistent, and not depending on varying environmental situations like the current user's locale, especially since I18n is usually related to views. I've got a few questions about this though:
Do you think this is a good rule in general?
How should I manage error messages in custom validation methods? Should I make exceptions for that scenario?
How should I manage customised keys that we use in form fields, such as the displayed text of a collection_select, that are partially translated? Is there a way to use view helpers for these occasions, that is (almost) as convenient as building model methods for these?
Yes. Translations belong to the view layer and only to the "display" part of it (that is, not to the API, which is still view)
Error messsages should be standard enough so you get to translate them correctly by iterating them in the view and getting the piecewise translations. Unfortunately that's not always possible because of different grammatical order of the sentences in languages. Still it's not useful to move those translations to the model. ActiveRecord's Errors class has hooks for translations just like ActiveModel, so the view can rely on them without injecting any i18n in the model itself.
Select helpers can often take a lambda as a label generator, you can use and abuse view paths and relative i18n keys and whatever you need without having to inject anything in the model. In case of doubt, use a presenter and inject I18n in it.

Rails query string parameter format when providing an API with filters

I'm opening up a few REST API calls to others to use. Some of them include search filters.
Let's say I have a grocery endpoint When I currently make calls I might use:
/grocery_items/index.json?types[]=fruit&types[]=deli
Leaving me with params[:types] as a nice collection.
However to make things easier for the folks consuming my API I want to be able to offer something like this:
/grocery_items/index.json?types=fruit,deli
Seems trivial to just split my params into a collection but I'm wondering if there are pitfalls since this seems to be against the grain of how rails expects collections to arrive as params.
I don't see anything wrong with doing a quick params[:types].split(',') to make calling your API easier to use. It's pretty common to do tricks with the query string, and this is a really tame change.

Rails security concerns

I am a Java programmer mostly, and it's actually amazing that we don't have to worry about a lot of security concerns that php or even rails developers have to worry about. We have to worry about them, but I think our job is actually a lot easier. You just use Java (already big bonus points there) and use Spring with Spring security... and you're basically done. Java and servlets are actually really good in this respect.
Now that I'm working in Rails, I think the biggest security concerns that I am the most scared of are parameters - both in the ones that are coming from the controllers (since they dynamic hashes, unlike in SpringMVC) and having to include more hidden values in forms.
But that got me thinking - you really have to be careful what you accept when you create new models or even update models. If you just blindly pass in parameters to your models, bad things can happen. In fact, things like the user role and stuff could be changed if you're not too careful.
It's almost like I want to write the setter code by hand to make sure it's not overwriting something that it shouldn't. And even if there's a framework mechanism to handle this... I would still want to test every risky model attribute just to be extra sure that it won't get overwritten on a create and on an update.
As much as Java gets a bad rep about productivity, it feels like it handles this stuff a lot better.
Anyway, my question is - what is the best resource/tips/advice for dealing with common security pitfalls/concerns/gotchas using rails - especially geared towards a Java/Spring developer who got used to working in a more stateful environment.
Even better, what would be a good checklist to go through every once in awhile?
And last, what tests would you recommend to make sure things are solid?
At least for your concern about assigning data to your model objects without proper checking, look into the attr_accessible declaration; it allows only specified attributes to be assigned via the bulk assignment:
user = User.new(params[:user])
user.approved = params[:user][:approved]
user.role = params[:user][:role]
You might find the entire 27th chapter of the Ruby on Rails 3rd edition book useful. (I haven't updated my 4th Edition book yet, not sure which chapter to recommend from the newer book. :)
I don't use ActiveRecord (I use DataMapper), but as a rule, I never do mass-assignment and I always expressly pass only the attributes I want to change. Rails 3 defaults to escaping all content in your views, unless you expressly output that data raw into into the .erb.
Also, it really bugs me that ActiveRecord doesn't help you out very much if you need to drop down to using SQL for something. You have to escape input yourself, which can expose you to the risk of human error allowing arbitrary SQL to be executed in your queries. DataMapper's underlying DataObjects connection supports prepared statements out of the box and in fact, it would actually require more work to avoid using them.
Rails 3 does have CSRF protection turn on by default too. It also makes session cookies HTTP-only by default, which makes them harder to steal via JavaScript.
I actually think, aside from Rails encouraging the use of mass-assignment, you're pretty well-covered for security.

Resources