Dynamically set a global variable in rails - ruby-on-rails

I was wondering how to initialize and set a global variable in rails. For example if I was building a pizza delivery system and I want the admin to be able to "close" and "open" the place whenever he pleases.

Setting a global variable is simple, just set it $open = false but that won't help you much in a live application because your application will probably be running across multiple processes (each with its own memory, and therefore its own global variables).
The simplest place to start is to just store this state in your database, and check it on each request that comes in where it's relevant.

$pizza_store = :open
That's all. It's global so it doesn't need to be in any sort of namespace, but I would rethink using global variables for any reason.
Start with code school or something similar to learn basic Ruby, then try a more complete tutorial (the Michael Hartl one is good) and learn 'the Rails way' - because if you want to do Rails, you have to do it their way, or you're going to quickly get frustrated.

In addiction to #smathy's answer, if you wanted to avoid using database, simply File.open a index.html in your /public folder, but, of course, you won't be able to use any dynamically generated content on that page.

Related

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

clarifying rails find method

I call the rails find method a lot during my view. Is this a generally accepted good practice? I'm leaning towards no, but why? Does find call the database each time? Does it cache the result anywhere? I looked up the ruby/rails docs on find but they didn't specify if it actually made a call to the database each time or not.
No, it is not a good idea to call find from the view. It is the job of a controller to load and assemble all the data that a view will need, and pass it to the view for presentation to the user.
Repeated calls to find for the same object should be cached by Rails, so it wouldn't hit the database each time, unless the arguments or other parameters were different.
A realtively easy way to test this would be to fire up a terminal and run rails c. In the console, run the code that returns tplangroup and play with it in the console. You should be able to see database call in the development console.
Based on my experience (not at a computer with rails right now) I think it would hit the database in each of these calls, unless you created tplangroup using the .includes(:tplan) for eager loading.
Hope this helps

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.

Ruby on Rails - Global Variable?

I am a new Ruby on Rails user and had a question. I have an idea of what I want my Users DB to look like but was wondering whether or not I should add an additional value to it. Basically I need a variable to signal to all users that it is safe to proceed with a certain action. This variable would be persistent across all users and should be visible to all users, but I want the server to be able to change this variable as well. When programming in other languages, I would use a global variables, so I wanted to check if that is also the case here. If so, would this be the best approach for going about it: Site-Wide Global Variables in Ruby on Rails. Also, how would I update the global variables. Thanks for any help!
A global variable doesn't fit your need. It doesn't spread across all the Ruby processes. If your web server spawns 5 ruby processes to handle 5 request at the same time, the variable defined in the first process won't be visible to the others.
There are other solutions available. You can use a database and store the flag/information on the database. Otherwise, you can use a file and store the value in the file.
The best solution would be an in-memory shared data source, such as memcached or Redis.

How can i 'insert' a small ruby app into Ruby on Rails to make a web page?

I have a small ruby application that I made on my local machine. It uses a text file insted of a database. It's a simple app that takes in a word, processes it against the text file, and then outputs the results using puts.
I would like to fit it into a RoR framework, hosted on my personal machine. I have run through some lessons and tutorials in a few books and online materials I have, but they all involve databases.
I read thru the notes in config/environment.rb and found at line 19 instructions to uncomment the line that removes ActiveRecord.
I am currently looking for the appropriate directories to put the text file itself, and the code from the ruby app that reads this text file. Thank you.
You probably want to do something like the following:
Have a controller that takes the word as a parameter.
Turn you app into a function that takes the word as a parameter and returns the results (instead of doing puts)
Call the function from your controller and render the results (e.g. something like:
render :text => my_func(word)
This sounds like a better fit for something more lightweight like Sinatra.
If you were bound and determined to use Rails, you could make a controller with an action that just runs the code from your program, but it seems like overkill. You wouldn't be using 99.9% of the framework's capabilities, so why is it even there?
If your just trying to give it a little interface you could look at Shoes which is an easy to use multi-platform gui framework.
Or like someone mentioned take a look at Sinatra.
You can simply use rails without worrying about ActiveRecord. I'd sugest making your little application into a class and requiring the file in a controller you want to use (or in environment.rb). Put the file in lib and if the data is temporary, "tmp" is fine or just put it in "lib" with the script you wrote even "db" is a fine location for it. To make a view just run your code and put the return in a class variable and make a view for it.
A model need not inherit from ActiveRecord::Base. Or anything else. As long as it follows the naming convention for models, Rails will pick it up without problem.
Having said that, if you're really looking at a one-model, one-controller, one-action, no-database app, then Sinatra would probably be a really good lightweight place to start...

Resources