I am building a Rails application where I need to do a usability test of three different Views for the same application. My thought is to switch out the default view path depending on the subdomain.
For example, I'd like to be able to define the paths something like:
option1.mysite.com => views/option_1
option2.mysite.com => views/option_2
option3.mysite.com => views/option_3
I'd like to keep the Models and Controllers the same, but switch out the Views depending on the subdomain. What might be the best way to do this?
We do it something like this:
session[:site] = case request.subdomains.last
when "a" then "a"
when "b" then "b"
when "c" then "c"
end
That's part of a set_site method in our application controller. Every request checks to see if session[:site] is set; if not, it calls set_site to set it.
In your case, now you just need to introduce logic in your views to present things differently depending on the value of session[:site], but it's even better if your actual view HTML is the same and the major difference is in the CSS. Then you just load different CSS files depending on the value of session[:site].
Related
What is the best way to structure a route for comparing multiple items?
Here's the URL example: https://versus.com/en/microsoft-teams-vs-slack-vs-somalia
How to achieve this in routes.rb file? Cannot really find anything in Internet regarding ruby gems. The only thing I can think about is url with optional params, however what if the number of params is unlimited?
you're going to have to parse the a-vs-b-vs-c yourself.
So in routes.rb, you'll have something like:
get 'compare/:compare_string', to 'compare#show'
then you'll get a parameter compare_string that you'll have to parse:
#in compare_controller.rb
def show
compare_items = params[:compare_string].split('-vs-')
# generate the comparison from the compare_items array
end
First - you probably shouldn't allow unlimited #'s of parameters in practice. Even something like 100 might break your page and/or cause performance issues and open you up to DOS attacks. I'd choose some kind of sensible/practical limit and document/enforce it (like 10, 12 or whatever makes sense for your application). At around 2k characters you'll start running into URL-length issues.
Next - is there any flexibility in the URL? Names tend to change so if you want URL's to work over time you'll need to slug-ify each of them (with something like friendly-id) so you can track changes over time. For example - could you use an immutable/unique ID AND human-readable names?
In any case, Rails provides a very flexible system for URL routing. You can read more about the various options / configurations with their Rails routing documentation.
By default a Dynamic Segment supports text like in your example, so (depending on your controller name) you can do something like:
get 'en/:items', to: 'items#compare'
If it's helpful you can add a custom constraint regexp to guarantee that the parameter looks like what you expect (e.g. word-with-dashes-vs-another-vs-something-else)
get 'en/:items', to: 'items#compare', constraints: { items: /(?:(?:[A-Z-]+)vs)+(?:[A-Z-]+)/ }
Then, in your controller, you can parse out the separate strings however you want. Something like...
def compare
items = params[:items].split('-vs-')
end
I made some regular expressions for email, bitmessage etc. and put them as constants to
#config/initializers/regexps.rb
REGEXP_EMAIL = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
REGEXP_BITMESSAGE = /\ABM-[a-zA-Z1-9&&[^OIl]]{32,34}\z/
and use it like
if #user.contact =~ REGEXP_EMAIL
elsif #user.contact =~ REGEXP_BITMESSAGE
Is that's good practice? What's the best way to store them?
It makes sense, that's one of the possible approaches. The only downside of this approach, is that the constants will pollute the global namespace.
The approach that I normally prefer is to define them inside the application namespace.
Assuming your application is called Fooapp, then you already have a Fooapp module defined by Rails (see config/application).
I normally create a fooapp.rb file inside lib like the following
module Fooapp
end
and I drop the constants inside. Also make sure to require it at the bottom of you application.rb file
require 'fooapp'
Lazy-loading of the file will not work in this case, because the Fooapp module is already defined.
When the number of constants become large enough, you can more them into a separate file, for example /lib/fooapp/constants.rb. This last step is just a trivial improvement to group all the constants into one simple place (I tend to use constants a lot to replace magic numbers or for optimization, despite Ruby 2.1 Frozen String literal improvements will probably let me remove several constants).
One more thing. In your case, if the regexp is specific to one model, you can store it inside the model itself and create a model method
class User
REGEXP_EMAIL = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
REGEXP_BITMESSAGE = /\ABM-[123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ]{32,34}\z/
def contact_is_email?
contact =~ REGEXP_EMAIL
end
end
Lets say, for the sake of the question, that I have two user types: type1 & type2. I want Rails to use a controller/module depending on the type of user that is being displayed. For example:
If User(id: 1, type: 'type1') has type1 and User(id: 2, type: 'type2') has type2, going to:
/users/1
would select the Type1::UsersController. And going to:
/users/2
would select the Type2::UsersController.
This will allow me to use different controllers and views for each type.
Note: I don't want the type to be displayed in the URL, I want it to be dynamic.
As GoGoCarl says, this isn't really the Rails way to do things. That said, it's not that difficult to get it to work. You can do something like this in routes.rb:
get 'users/:id', to: 'type1/users#show', constraints: lambda { |request|
_id = request.fullpath.gsub('/users/','').to_i
# Note: there might be an easier way to get ID from the request object
User.find(_id)._type == 'type1'
}
get 'users/:id', to: 'type2/users#show', constraints: lambda { |request|
_id = request.fullpath.gsub('/users/','').to_i
User.find(_id)._type == 'type2'
}
I've renamed your type field to _type in my example (because Rails uses type for Single Table Inheritance). I've tested this and it works as desired.
This is possible, but you'd be doing a lot of (probably) unnecessary fighting against the Rails way. I would think you would want one controller as there's probably quite a bit of shared logic (such as saving, deleting, creation, etc).
To answer your question (because I hate when people leave recommendations instead of answers), then you'll need to create a Module that extends Routing, which will allow you to do custom matching. From there, you can do your checks and route appropriately. Here's an example.
That said, a better route to go (no pun intended) would be to have one controller which has a centralized method that can select views.
def find_view view_name
"#{view_name}#{#user.type}"
end
So, a call to render find_view('new') would attempt to render a view named "new-type1." You can put all your type1 user-specific logic in that view. Same for user type2.
Again, since I would think there would be much overlap in your user code, you may want to push this find_view method to a helper class so you can call it from your views, and do things like render specific partials instead based on the user type. That will allow for more code re-use, which is never a bad thing.
Once you get your head wrapped around having a single controller, there are a number of simple ways that you can push user-type-specific code to different avenues -- the views method explained above, you can push all your relevant code to separate helpers which are dynamically called based on the user type, and I'm sure there's more (probably better ones). But all those have one major thing in common -- you'll be fighting Rails a LOT less, and you will have less duplicate code, if you succumb to letting Rails have its way with one route, one controller.
Good luck, hope that helps.
I have a controller which has a lot of options being sent to it via a form and I'm wondering how best to separate them out as they are not all being used simultaneously. Ie sometimes no, tags, sometimes no price specified. For prices I have a default price set so I can work around with it always being there, but the tags either need to be there, or not. etc.
#locations = Location.find(params[:id])
#location = #locations.places.active.where("cache_price BETWEEN ? AND ?",price_low,price_high).tagged_with([params[:tags]).order(params[:sort]).paginate :page => params[:page]
I haven't seen any good examples of this, but I'm sure it must happen often... any suggestions? Also, even will_paginate which gets tacked on last should be optional as the results either go to a list or to a google map, and the map needs no pagination.
the first thing to do when refactoring a complex search action is to use an anonymous scope.
Ie :
fruits = Fruit.scoped
fruits = fruits.where(:colour => 'red') if options[:red_only]
fruits = fruits.where(:size => 'big') if options[:big_only]
fruits = fruits.limit(10) if options[:only_first]
...
If the action controller still remains too big, you may use a class to handle the search. Moreover, by using a class with Rails 3 and ActiveModel you'll also be able to use validations if you want...
Take a look at one of my plugins : http://github.com/novagile/basic_active_model that allows you to easily create classes that may be used in forms.
Also take a look at http://github.com/novagile/scoped-search another plugin more specialized in creating search objects by using the scopes of a model.
More and more I'm putting all of my code in models and helpers concerning MVC.
However, sometimes I'm not sure where to organize code. Should it go into the model or should it go into a helper. What are the benefits of each. Is one faster or are they the same. I've heard something about all models getting cached so it seems then like that would be a better place to put most of my code.
For example here is a scenario that works in a model or in helper:
def status
if self.purchased
"Purchased"
elsif self.confirmed
"Confirmed"
elsif self.reserved
"Reserved"
else
"Pending"
end
end
I don't need to save this status as in the database because there are boolean fields for purchased, and confirmed, and reserved. So why put this in a model or why put it into a helper?
So I'm not sure of the best practice or benefits gained on putting code into a model or into helper if it can be in both.
Your specific example is including a business rule in the sense that if the instance of the model is both purchased and confirmed then the proper status is "purchased" not "confirmed"
So in your example, I'd definitely put the method in the model since it is coding one of your applications business rules.
A different example:
def status_string
case status
when 0: "Purchased"
when 1: "Confirmed"
else
"Pending"
end
end
In this case, the status_string method could be reasonably defined either in a View Helper or Model--it has nothing to do with any business rules, it is changing the representation of the value. I'd put it in the model since I tend to only put html-related sw into the View Helpers. But depending on your internationalization scheme, a similar method might be better placed in the View Helper.
A good example of a View Helper is an application-wide method to transform date time values into the standard representation for your app. Eg
# application_helper.rb
def date_long_s(d)
d.strftime("%A, %b *%d, %Y *%I:%M %p")
end
This is really subjective and I agree, sometimes it is not clear if something belongs in a model or helper.
For example:
# using model
status ? status.nice_name : "Pending"
# using helper
nice_name(status)
The clear advantage here for the helper is that it can handle nil objects gracefully keeping views clean. The disadvantage is that the code is now in a different location away from the model
Performance wise you will not see any significant difference between using helpers and models. It is more likely that DB round trips to pull status objects will be a bottleneck.
I use constant hashes in this kind of situations.
Hash is defined in model file like this
STATUS = {
1 => "Pending",
2 => "Confirmed"
}
I also declare constants for each status like this.
ST_PENDING = 1
Declaring this is useful when writing queries. For example,
MyModel.all(:status=>ST_PENDING)
status field in database table is number.So when printing, I simply use this.
MyModel::STATUS[obj.status]