Rails, handle two sites with different url and design but with the same db - ruby-on-rails

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).

Related

Rails same app with different databases

I'm building a report app in Rails for a current desktop app. The app is kind of pos/accounting.
So at the moment there are different clients with the desktop app installed at shop or office. Each one has its own mysql database.
I am now planning how to setup the server, most probably heroku.
Given that I will keep the databases for each client separated, I'm trying to understand the best path to follow.
Another thing to consider is that there will be different version of the app, i.e. restaurant, bar, shop etc.. Different versions will use the same database. I'll just need to change some controller and view. Most probably I'll handle this using namespace.
For a client(company) multiple users will have access to the app.
The solution I thought is to create a database table company and add a column company to users table.
Things that could change from a client to another:
call different images from assets such as logo
there might be custom views or even controller if a client requests some customization
use different database
domain possibly, but that's not that important wouldn't a be a big deal to use the same
Then based on user.company show a different logo or any other required image, render a view instead of another, at login connect to the proper database.
I don't know yet how to implement it, right now I'm still evaluating the best approach to follow. But this way maintenance/updates would be easy to manage even if maybe performance will be a bit lower and code harder to understand with possible customizations all together but that's acceptable.
Is this a reasonable solution or should I consider something different? Am I missing something important to consider?

Keep value in memory across requests and across users in Rails controller? Use class variable?

We're on Rails 3.0.6.
We maintain a list of numbers that changes only once a month, but nearly every page request requires access to this list.
We store the list in the database.
Instead of hitting the database on every request and grabbing the list, we would like to grab the data once and stash it in memory for efficient access.
If we store the list in each user session, we still need to hit the database for each session.
Is there a way to only hit the database once and let the values persist in memory across all users and all sessions? We need access to the list from the controller. Should we define a class variable in the controller?
Thanks!
I think Rails.cache is the answer to your problem here. It's a simple interface with multiple backends, the default stores the cache in memory, but if you're already using Memcached, Redis or similar in your app you can plug it into those instead.
Try throwing something similar to this in your ApplicationController
def list_of_numbers
#list_of_numbers ||= Rails.cache.fetch(:list_of_numbers, :expires_in => 24.hours) do
# Read from database
end
end
It will try to read from the cache, but if it doesn't find it, will do the intensive stuff and store it for next time
The pattern you're looking for is known as a singleton which is a simple way to cache stuff that doesn't change over time, for example, you'll often see something like this in application_controller.rb -- your code always calls the method
def current_user(user_id)
#current_user ||= User.find user_id
end
When it does, it checks the instance variable #current_user and returns it if not nil, otherwise it does the database lookup and assigns the result to the instance variable, which it returns.
Your problem is similar, but broader, since it applies to all instances.
One solution is with a class variable, which is documented here http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html#S3 -- a similar solution to the one above applies here.
This might be a good solution in your case, but has some issues. In specific, (assuming this is a web app) depending on your configuration, you may have multiple instances of Rails loaded in different processes, and class variables only apply to their specific instance. The popular Passenger module (for Apache and Nginx) can be configured to allow class variables to be accessible to all of it's instances ... which works great if you have only one server.
But when you have multiple servers, things get a little tricky. Sure, you could use a class variable and accept that you'll have to make one hit to the database for each server. This works great except for the when that the variable ... varies! You'll need some way of invalidating the variable across all servers. Depending on how critical the it is, this could create various very gnarly and difficult to track down errors (I learned the hard way :-).
Enter memcached. This is a wonderful tool that is a general purpose caching tool. It's very lightweight, and very, very smart. In particular, it can create distributed caches across a cluster of servers -- the value is only ever stored once (thus avoiding the synchronization problem noted above) and each server knows which server to look on to find any given cache key. It even handles when servers go down and all sorts of other unpleasantries.
Setup is remarkably easy, and Rails almost assumes you'll use it for your various caching needs, and the Rails gem just makes it as simple as pie.
On the assumption that there will be other opportunities to cache stuff that might not be as simple as a value you can store in a class variable, that's probably the first place to start.

Building consumable uri/urls for a model (rails/datamapper/SOA)

Perhaps you can help me think this through to greater detail.
I need to build or make available a uri for a model instance that can be referenced or used by another application which may or may not be a rails application.
e.g.
I create a standard Post with content; I want to build a URL for that post another application can consume or reference by looking at the model in the database (or another less sticky fashion). Datamapper has a URI field, I want to build a canonical uri, store it there and have another application be able to access, announce, manipulate, etc.
Basically, I have several applications that may be in different places, that need to access the same model, to do differing things with the model. I need a way to make that happen clearly without putting them all in one monster application.
I've looked at Pubsubhub, RSS, etc. but haven't found any concrete examples of what I'm trying to do. Do I need to create an common API for the applications, etc?
DataMapper is very flexible about using existing databases.
Many people come to DataMapper because it can create and tear down the database structures without migrations. However, you do not have to work with it in that way.
I have had good success with using a large set of models owned by a central 'housekeeping' app and then declaring a small subset of the same models in separate 'interface' apps.
Some trial and error is required to figure out what works but it can certainly be done. I'd suggest putting your models in modules and including them across apps if possible.
A final point it sounds like you want URIs/URLs to be the primary interface. If that is the case I strongly suggest you look at Sinatra. It is entirely oriented around URLs (and I find Rails routes very obtuse).

Multi domain rails app. How to intelligently use MVC?

Background:
We have app a, b, and plan to add more apps into this same application. The apps are similar enough they could share many views, assets, and actions. Currently a,b live in a single rails app(2.3.10). c will be similar enough that it could also be in this rails app.
The problem:
As we continue to add more apps to this one app, there's going to be too much case logic that the app will soon become a nightmare to maintain. There will also be potential namespace issues. However, the apps are very similar in function and layout, it also makes sense to keep them in one app so that it's one app to maintain(since roughly 50% of site look/functionality will be shared).
What we are trying to do is keep this as clean as possible so it's easy for multiple teams to work on and easy to maintain.
Some things we've thought about/are trying:
Engines. Make each app an engine. This would let us base routes on the domain. It also allows us to pull out controllers, models and views for the specific app. This solution does not seem ideal as we won't be reusing the apps any time soon. And explicitly stating the host in the routes doesn't seem right.
Skinning/themes. The auth logic would be different between the apps. Each user model would be different. So it's not just a skinning problem.
In app/view add folder sitea for sitea views, siteb for siteb views and so on. Do the same for controllers and models. This is still pretty messy and since it didn't follow naming conventions, it did not work with rails so nicely and made much of the code messier.
Making another rails app. We just didn't want to maintain the same controller or view in 2 apps if they are identical.
What we want to do is make the app intelligently use a controller based on the host. So there would be a sessions controller for each app, and perhaps some parent session controller for shared logic(not needed now). In each of these session controllers, it handles authentication for that specific app. So if the domain is a.mysite.com, it would use session controller for app a and know to use app a's views,models,controllers. And if the domain is b.mysite, it would use the session controller for b. And there would be a user model for a and user model for b, which also would be determined by the domain.
Does anyone have any suggestions or experience with this situation? And ideally using rails 2.3.x as updating to rails 3 isn't an option right now.
Devise does exactly this. You would do well to check out its architecture and apply that architecture to your own case.
You will have multiple separate Rails applications. The shared code will be a separate project, perhaps distributed as a gem or at least a separate Git repository. The shared code will include many controller actions and many view templates that are there to be sensible defaults, and which will be overridden in some apps but not in others.
All the custom code for application A will belong in a project solely devoted to containing the custom code for application A. It will be its own fully-functioning Rails application and will depend heavily on the majority of the sensible defaults provided by the shared code in the shared-code project.
I've used the theme support plugin before and dynamically set the theme based on the request uri:
http://mattmccray.com/svn/rails/plugins/theme_support
It will probably need some work to support Rails 2.3.
Update: Looks like there's a rewrite: https://github.com/dasil003/rails-multisite
Sounds like you want to make the 'base' app a plugin and use that in each of your site apps. You can use something like svn-extern so it's automatically updated whenever something changes.

How to turn a single-site app into a mantainable multi-site app without code changes?

It's an application that we use internally at the office that I would like to offer as a hosted service for anyone.
How can I do that without making major code changes?
The first thing that occurs to me is to have the app select which database to connect to based on the domain.
So each instance of the app would have its own database, but all instances would share the same code.
The only changes required to the code would be the database selection.
Is this approach maintainable? I've heard wordpress.com does this and that it offers a couple of advantages. I'm mainly looking to do it this way to avoid have to scope my entire set of database queries to a certain site within the same database.
Thanks!
The simplest way to do this is to clone the application, and create another server instance to handle it. This actually the way I handle multiple wordpress blogs on my server
Pro:
This process can be streamlined into a utility script.
Can be easily maintained if symlinks are used for the common code. IE: Everything but branding and some of the things in the config directory.
Cons:
- If you're using passenger it will require an apache restart for each new instance.
- Same if you're using Apache to route subdomains on different virtual hosts to different mongrel clusters.
However the better way comes from the question: Rails - Separate Database Per Subdomain
The method in the accepted answer is much more robust. It might require more changes than you're looking for, but it has all the benefits without the drawbacks of any other methods. Each new instance requires a new entry in the master database with the table name and other instance specific information. You'll also want custom rake task to build the database for each new instance.
I would suggest switching the database connection and adding a view_path based on the domain, I have posted code in this question.
I hope this helps!
I wouldn't do this with multiple databases as you mentioned. Keeping all your schemas/migrations in sync with all the db's could become painful.
I would look into simply making it a multi-tenant app where you have some sort of "Account" model and then all your existing models are scoped to it ... in other words, if this was a blog app, your Account has_many :posts, etc.
With this approach, you can identify accounts by subdomain ... have people choose their subdomain when they create an account and go from there.
It's pretty straightforward to do. If you need add billing into the mix, you might look at the SaaS Railskit (which handles all the signup and subdomain stuff) or Chargify.
You can also identify accounts Twitter-style ... with http://myapp.com/someuser

Resources