Background:
I'm building an app that lets users create their own website (e.g., mywebsite.alexlod.com)
Each website owner is affiliated with me, so I'll trust them to write their own rails code
Each website should have default controllers and views, except when one of these owners creates their own
Here's how I envision controllers working:
I'm thinking that what's in app/controllers/ will be the default, but when a file is specified in a <subdomain> top-level directory, that file will take precedence over the default.
Let me give an example. Let's say one of my website owners (foo.alexlod.com) wants to tweak app/controllers/photos_controller.rb. They should be able to create foo/controllers/photos_controller.rb whereby their controller is used instead of the default. I'm thinking the correct approach here has something to do with routes and the load path, but I'm new to Rails and Ruby and could use some guidance.
As for views, I would like them to work much the same. When a view or partial is defined in <subdomain>/views/, that view is used instead of the default located in app/views/.
I realize my plan here violates the default rails directory structure. But this approach has gotta be more simple than the alternative - case statements in each controller action. Unless there's an even better alternative?
I believe engines may be the way to go for what you want. That link is a little old but still should be applicable with Rails 3.1. Each subdomain's controllers and views can be placed in their respective engine folder.
Now, you just need to figure out which engine to load per request. That's the part I think may be the deterrent to your idea -- you don't want to be loading and unloading code all the time in production.
You may want to opt for a templating language like Liquid, in that case. That, however, gives a lot less control to the users.
I may have a partial answer for you as it sounds like you want to do something very similar to what I am doing for the mobile version of my site. After I identify that a user is mobile I add a mobile directory to the path to override any views that I have optimized for mobile. If the view doesn't exist in the mobile directory it defaults to the default view.
Here is what I did for views:
in app/controllers/application_controller.rb
before_filter :prepend_view_path_if_subdomain
def prepend_view_path_if_subdomain
unless pSubdomains.blank?
subdomain = request.subdomain.first
#This will add the subdomain view directory to the view path before the default
#Rails view directory and any views here will be picked up and rendered.
prepend_view_path 'app/' + subdomain + '/views'
end
end
Doing the same thing for controllers, however, is a little trickier due to routing. There is no prepend_controller_path method that is equivalent to prepend_view_path. Honestly I'm not sure how to approach this, you could use your case statement approach or possibly dynamically forward on the request to the subdomain controller (it if exists). I think it might be possible to add a before_filter to your controllers that evaluates each request much like I show above with views and then determines which controller should be used.
I also stumbled across this question: Rails 3.1 load controller from different path based on subdomain, not sure if it helps you or not.
Related
I am using Rails version 2.3.14 and I am trying to figure out the best way to use a different layout for an entirely new section of my site but I can't think of the most efficient way to do it.
e.g. I have a CRM and everything is localhost/controller/method as normal but what I want to do is create a sub-section of my site named portal.
I considered creating a new app but I don't really want to do that since this sub section will be using existing controllers so I don't want to end up in the situation of having to remember to update both controllers to make sure their contents are identical as that will end up getting messy.
I read this answer rails 3, how add a view that does not use same layout as rest of app? and it makes sense but I can't think how to apply that to my situation.
e.g. http://localhost/ would show my existing login page and something like localhost/portal would show the login page for the new section but with a completely different layout but using the same models and controllers.
I'm new to rails so I might be over-thinking it or I might be barking up the wrong tree.
If it's possible I think the easiest way would be to somehow check the url for portal and set the layout based on the outcome of that check.
I read the answer you posted, it makes sense, I just try to give you some real codes in your case.
You have a sub site, it means that your path of all pages in sub site will start with '/portal', right?
In your app/views/layout , you can add a layout named "portal_subsite.html.erb".
In your application_controller.rb
class ApplicationController < ActionController::Base
layout :get_layout
private
def get_layout
if request.path.start_with?('/portal')
'portal_subsite'
else
'application'
end
end
end
I have not a environment of Rails 2 now, but I think it should work, if you have any questions, please comment below.
In a Rails app I'm creating, I have some code in my controller that I'm wondering whether it's in the proper place. The code is fairly insignificant, it stores ids in an array to show 'recently viewed' pages. It's about 3 lines of code, but I'm thinking to the future, what if this feature expands? I don't want my controller to be bloated.
I could make a module, but in that case where should I store the file?
Is the controller the right place to be doing the session management?
Any suggestions on my code organization?
Thanks
If it is specific to the controller, keep it in the controller.
If it applies to all controllers, it goes in ApplicationController.
If it is shared by some controllers and not others, then inherit from a controller that inherits from ApplicationController, or use include/extend or make a module that extends ActiveSupport::Concern (which is what Rails uses internally fairly commonly).
And it's best to keep everything in app/controllers or some subdirectory, sub-subdirectory, etc. Rails autoloading depends on the path to match up with the module namespace, so A::B::C belongs in app/controllers/a/b/c.rb. Don't make it deep like Java, etc. Just have the number of directories/modules you need to keep it organized.
Note: though controllers aren't as problematic to have in their own modules, in my experience your models should stay in the root, like app/models, or you'll have problems.
I'd also avoid storing too much in session if you can help it. Store in the DB (or long-life cookies, if it is browser-environment specific) instead. For example- if someone logs out and they were looking at one record, they might want to log back in later and have a list containing the link to that record.
BTW- you weren't asking and probably already have the code for storing recently visited pages in session, but here are similar questions/answers:
https://stackoverflow.com/a/10813602/178651
http://www.rorexperts.com/how-to-store-visited-pages-in-session-object-in-rails-t941.html
Hey guys and merry christmas!
I'm new to ruby on rails and I'm still a little bit confused about some stuff:
When do I need to create a new controller and when not?
I want to create an app with a single searchbox and search through all of the articles. Should I create an Controller for the startpage (the searchbox) and for the search? Should I create controllers for the static pages?
Should I use an Admin interface gem or create my own?
The normal user should now have access to creating articles, just the admin. Should I use one of the admin interface gems or create my own?
Ruby on Rails follows the MVC framework, controllers are classes that contain your actions, so you need to add an action for every function your website will provide.
Technically you could have all actions in one controller but that would be just terrible, so we usually create different controllers to organize your routes and code in a better way.
Follow the Rails guide on controllers.
For the admin interface gem, you could use devise and cancan, they are both very reliable and well tested.
Ruby on Rails is indeed MVC, which means that controllers connect Models to Views. So in general it is good practice to think more resource-oriented: per resource you want found/presented, you create a controller. In your case something like:
ArticlesController : your main view, with the searchbox
PagesController : for static pages, if you need some erb/haml
admin/ArticlesController: for administration of the articles
Now, completely static pages can just as well be placed under the public folder, no need for a controller unless you need some dynamic info to be on the pages (e.g. a total count of articles).
With regards to your search-box: imho this is just a parameter for your index page. E.g. on the index you show the ten most recent articles, and when searching on some term, you show the relevant articles, but on the same controller and the same action.
With regards to the admin interface: yes, use gems like rails_admin or active_admin and it will get you started in no time at all. So definitely do that. But those gems are, of course, very general and might not suit your needs completely. It that should be the case, you can always easily revert later.
HTH.
Merry christmas!
As Khaled suggested Rails being MVC architecture it is always good to have controllers of each page. Even though you might have a static pages for now, but latter when you are trying to make the site dynamic one, then you will be in whole lot confusion of where to add a method for a particular view page.
Generally it is better to use a gem instead of making it from scratch.
You can look into this link which teaches you how to use devise and cancan with twitter bootstrap(for views). But if you are planning to learn rails then I better recommend you to do it from scratch as you will have an idea of what is happening. You can see this tutorial which does most of the task through scratch.
Enjoy Rails!!
I am developing a community / news article website where there is a side column with different "blocks" on nearly all pages. In these blocks is "Recent Articles (showing five most recent articles)," "Recent Blogs," "Recent Comments," you get the drift.
When I started out building the application, I wasn't real sure where to put the controller code (say, to call #recent_articles = Article.where...etc). I didn't think it could go into the Articles controller, because it's not always the Articles controller being called. So I thought it would work best in the application controller, as most content on the site would be calling this. I put "#recent_content" into the application controller, did a :before_filter to load it.
You might see the flaw in this. As I'm getting better with Rails, I went back to refactor as the site was loading horribly and sure enough, all my logic in the application controller defined by before_filter was being loaded on every action, no matter if it was needed or not. (The site sped up dramatically when I cleaned house on the application controller).
My mistake is realized, but I still need to define the instance variables for #recent_articles, #recent_blogs, etc somewhere, so they load up only when needed. Granted I'll be eventually caching the site content when it goes into production, but I want to be a good Rails programmer here.
So here is my question...exactly how would you handle this situation and where would you put the logic? I can think of two ways, not sure which one is better...
The first way...I took a look at a project from another Rails developer and I noticed he was doing odd things like this by creating files in the /lib folder. For example, defining a method for page meta tags or active menu states. I honestly haven't messed with the /lib folder before, figuring most of my stuff should stay in the /app folder.
The second way...seems to me like helpers might seem the way to go. Maybe I could define a "recent_articles" helper, call my #recent_articles instance variable in there, then render and pass the results to a view file in my shared folder.
Overall, which one of these ways is the better way to go, either from a performance or best-practices viewpoint? Or is there a better way of doing this that I'm unaware of?
Whenever there are many models that can call a particular method, i would probably use a module. I think that is what you are talking about in your first idea, since /lib is where modules are placed.
You can use helpers as well, but it's a good idea to keep logic out of helpers, only in models if possible. Helpers should be just used as a way to present data, they are part of views. If logic is added, then something is wrong :)
Make sure that you do not have logic in your controllers as well. I would be doing the same things in the beginning, but it's really a bad idea. Instead, put everything in your models, or maybe a module if they seem to be used by many other models.
Hope that helps you a bit :)
Let's say I have a rails app with content that's read-only to the public, but I'd like to build tools to edit that content (or use scaffolding). I don't want to expose create/update/delete actions publicly (even if password-protected), but I'd like to have a server with this functionality inside a local network that interacts with the production database.
So I'm thinking of writing a plugin for this, which would add a tools rails environment (like development, production, and test) and a way of configuring a controller method as "tools-only". When not in tools mode, requests for any tools-only action gets redirected to a standard 404 page.
Before I start reinventing a wheel, does something like this already exist? Are there perhaps better ways to solve this problem?
Interesting idea - Aside from creating the custom environment wouldn't it just be one before_filter in your Application controller?
before_filter :can_access_tools, :except => [:show, :index]
Other controllers would just skip it as need be.
Why do it as a separate environment? What I would do (and have done) is put the editing functionality in a separate namespace (I usually call it admin), then using a before filter only allow a user with an admin role access to those controllers. So if your app is displaying widgets, you could see them in the normal app behind the url /widgets. In order to edit them you would go to /admin/widgets (and also have to be logged in as an admin). Instead of just doing it via route namespaces, I also create an AdminController that all other controllers in that namespace extend. Then you can put any filters that need to apply to all of them in one place.
You could get the UI for this up pretty quick by dropping in something like Active Scaffold to provide a pretty clean interface to do CRUD operations on it. You could also take a look at Streamlined or Hobo for this part as well.
I often set up extra environments for testing multiple contexts, but for this case it seems like overkill. If you limit the editing functionality by authorization (requiring the role of admin) instead of by access (only the local server can get to it), it also allows you to make changes if you're on the road and not in the office.
umm...i know herokugarden allows you to edit your code online through a browser, will that work?
here is a link to the demo