Class variables VS constants in Rails models - ruby-on-rails

In my application there can be only one current Event which defaults to the nearest date event. I need to retrieve this event in various places and since it doesn't change it makes sense to cache it. There are two ways of doing it known to me:
class Event < ActiveRecord::Base
CURRENT_EVENT = Event.where('starts_on >= ?', Time.now).
order('starts_on ASC').limit(1).first
# OR
def self.current_event
##current_event ||= Event.where('starts_on >= ?', Time.now).
order('starts_on ASC').limit(1).first
end
end
Which one would be the best? Or any other alternatives? I know that using ## class variables is not recommended since they are not thread safe.

I guess you aren't right about your approach: this way your app will keep your cached value forever. New events won't affect it which is completely wrong. It may be the situation when some event already passed but it is still cached as "current".
By the way: limit(1).first does the same as the only first.

Neither first nor second approach are correct. If you define constant - it will find Event, actual on Rails initialization process time. Second approach will not cache your record.
As for me, this is not so fat data to cache.

Related

changing the value of a global variable in rails

I'm prototyping an app and want to have a global variable that I can hit an endpoint that toggles the global variable $current_status. I have:
def toggle_status
$current_status=false if $current_status.nil?
$current_status=!$current_status
r={}
r[:current_status]=$current_status
render json:r.to_json
end
and in application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
$current_status
end
but hitting /toggle_status always returns false. Why isn't assigning a bool to what it isn't changing this value? I'm aware something like this should be in db but just prototyping
edit 1
I just created this in lib/
class Jt
#cur
def self.cur
#cur
end
def self.cur=val
#cur=val
end
end
and updated the controller to:
def toggle_status
Jt.cur=!Jt.cur
r={}
r[:current_status]=Jt.cur
render json:r.to_json
end
Your toggle code doesn't actually toggle anything. It appears you expect this line to "toggle" the contents of the $current_status variable.
$current_status!=$current_status
However, the != operator doesn't assign anything but it is a comparison operator. In your case, it returns always false based on your query whether $current_status is equal to not $current_status.
What you want to use instead is probably
$current_status = !$current_status
As for your software design, global variables are generally frowned upon in Ruby (and Rails) as are all other kinds of globally mutable state. Use proper classes and objects instead to encapsulate your state and behaviour into more manageable structures. Using global variables, you will shoot yourself in the foot some day and you will have a very hard time to find out what is actually happening. You should try to avoid this :)
You can't use a global variable in this way in such app and there are several reasons. I'll give you just one: depending on the webserver you use, the server may start different processes to handle the incoming web requests, and global variables can't be shared between these processes.
And in fact, it's not even a good idea to use a global variable at all.
If you want to persist a state, use a proper storage. Depending on how long the value should be persisted and who should be able to access it, you have plenty of choices:
database
file system
memory
cookie
Your first snipper does not work because != is a comparison operator, not assignment
Second may not work due to code reloading (Jt class instance is not guaranteed to be the same for other request unless cache_classes is on, but in development you usually always want it off, because otherwise any code changes require server restart to take effect), simple way to have a non-reloaded class - put it in a initializer
For prototyping you also may try thread-local storage for this task: Thread.current[:foo] = 1234

Rails Limit Model To 1 Record

I am trying to create a section in my app where a user can update certain site wide attributes. An example is a sales tax percent. Even though this amount is relatively constant, it does change every few years.
Currently I have created a Globals model with attributes I want to keep track of. For example, to access these attributes where needed, I could simply do something like the following snippet.
(1+ Globals.first.sales_tax) * #item.total
What is the best way to handle variables that do not change often, and are applied site wide? If I use this method is there a way to limit the model to one record? A final but more sobering question.......Am I even on the right track?
Ok, so I've dealt with this before, as a design pattern, it is not the ideal way to do things IMO, but it can sometimes be the only way, especially if you don't have direct disk write access, as you would if deployed on Heroku. Here is the solution.
class Global < ActiveRecord::Base
validate :only_one
private
def only_one
if Global.count >= 1
errors.add :base, 'There can only be one global setting/your message here'
end
end
end
If you DO have direct disk access, you can create a YAML config file that you can read/write/dump to when a user edits a config variable.
For example, you could have a yaml file in config/locales/globals.yml
When you wanted to edit it, you could write
filepath = "#{Rails.root}/config/locales/globals.yml"
globals = YAML.load(File.read("#{Rails.root}/config/locales/globals.yml"))
globals.merge!({ sales_tax: 0.07 })
File.write(filepath) do |f|
f.write YAML.dump(globals)
end
More on the ruby yaml documentation
You could also use JSON, XML, or whatever markup language you want
It seems to me like you are pretty close, but depending on the data structure you end up with, I would change it to
(1+ Globals.last.sales_tax) * #item.total
and then build some type of interface that either:
Allows a user to create a new Globals object (perhaps duplicating the existing one) - the use case here being that there is some archive of when these things changed, although you could argue that this should really be a warehousing function (I'm not sure of the scope of your project).
Allows a user to update the existing Globals object using something like paper_trail to track the changes (in which case you might want validations like those presented by #Brian Wheeler).
Alternatively, you could pivot the Global object and instead use something like a kind or type column to delineate different values so that you would have:
(1+ Globals.where(kind: 'Colorado Sales Tax').last) * #item.total
and still build interfaces similar to the ones described above.
You can create a create a class and dump all your constants in it.
For instance:
class Global
#sales_tax = 0.9
def sales_tax
#sales_tax
end
end
and access it like:
Global.sales_tax
Or, you can define global variables something on the lines of this post

How to record time of last activity in Rails app?

In my Rails application I would like to record the time a user was last_seen.
Right now, I do this as follows in my SessionsHelper:
def sign_in(user)
.....
user.update_column(:last_seen, Time.zone.now)
self.current_user = user
end
But this is not very precise because a user might log in at 8 a.m. and in the evening the last_seen database column will still contain that time.
So I was thinking to update last_seen whenever the user takes an action:
class ApplicationController
before_filter :update_last_seen
private
def update_last_seen
current_user.last_seen = Time.zone.now
current_user.save
end
end
But I don't like that approach either because the database gets hit with every action that a user takes.
So what might be a better alternative to this?
Rails actually has this sort of behavior built in with touch:
User.last.touch
#=> User's updated_at is updated to the current time
The time it takes in any well-provisioned DB to handle updating a single column like this should be well under 5ms, and very likely under 1ms. Provided you're already going to be establishing that database connection (or, in Rails' case, using a previously established connection from a pool), the overhead is negligible.
To answer your question about whether your code is slower, well, you're thinking about this all wrong. You can optimize an already very fast operation for performance, but I instead you worry more about “rightness”. Here is the implementation of ActiveRecord's touch method:
def touch(name = nil)
attributes = timestamp_attributes_for_update_in_model
attributes << name if name
unless attributes.empty?
current_time = current_time_from_proper_timezone
changes = {}
attributes.each do |column|
changes[column.to_s] = write_attribute(column.to_s, current_time)
end
changes[self.class.locking_column] = increment_lock if locking_enabled?
#changed_attributes.except!(*changes.keys)
primary_key = self.class.primary_key
self.class.unscoped.update_all(changes, { primary_key => self[primary_key] }) == 1
end
end
Now you tell me, which is faster? Which is more correct?
Here, I'll give you a hint: thousands of people have used this implementation of touch and this very code has likely been run millions of times. Your code has been used by you alone, probably doesn't even have a test written, and doesn't have any peer review.
“But just because someone else uses it doesn't make it empirically better,” you argue. You're right, of course, but again it's missing the point: while you could go on building your application and making something other humans (your users) could use and benefit from, you are spinning your wheels here wondering what is better for the machine even though a good solution has been arrived upon by others.
To put a nail in the coffin, yes, your code is slower. It executes callbacks, does dirty tracking, and saves all changed attributes to the database. touch bypasses much of this, focusing on doing exactly the work needed to persist timestamp updates to your models.

Permanent variable in Rails

Lets say that on top of my Rails app there is a bar with piece of text displayed - latest hot deal, scheduled downtime notfication, something like that. It's a single, on of a kind information that needs to be accessed on basically every request, and may be updated from time to time. What is the best way to achieve this?
What I'd like to do is some kind of permanent global variable (accessible from controllers).
It will be updated very rarely, so there's no problem if for some time after update there will be an inconsistency between workers.
On the other hand, it should be persistent in case of server fault (periodic backup is enough).
It will be accessed really often, so it should be as fast as possible - preferably stay in memory.
Also, it's only one of a kind, so I'd really prefer not to bloat the app with a dedicated database model.
Something like that is damn easy in Node.js for example, but I couldn't find a single way to achieve this in Rails. What shall I do?
EDIT
Thanks for the answers so far, but while they're inspiring, I think that I should stress out one key functionality that they're all missing. The variable should be editable inside the app and persistent. While it's possible to edit your variables, in case of server restart I'm back to the default - which is bad.
It really depends on what you are looking for. You could do something very simply by putting it in your application_controller.rb
class ApplicationController < ActionController::Base
def system_message
"Come buy our amazing .99 iphone chocolate bar apps, with 100% more gamification!"
end
end
That function (and string) is then accessible from any controller in your application. You could also specify something in the after_initialize block in your application.rb file.
config.after_initialize do
::MYTEXT = "MY SUPER AMAZING TEXT"
end
You could also create your own file under the initializers directory, which is preloaded in rails.
so siteAnnounce.rb
MYANNOUNCEMENT = "NOW LISTEN TO ME!"
You may also want to check out this Railscast video about site wide announcements
I would store it in the database and let caching take care of it.
I feel that global variables are fine, when appropriate, for code that needs to share that common value in many places but that is the code, not the the user view.
This is clearly true in this case as the OP has bolded 'editable by the app'. So I would have a view that lets the users enter it, it gets stored in a db table and then recalled as needed (as cached once used once).
Well I had faced a similar problem.
My problem was I needed a global variable in all the levels (MVC).
We went to use Memcache to store the variable.
May be you can go for a similar solution.
And as an added bonus you can change it throughout the program.
You could declare it as a constant in an initializer:
config/initialzers/foo.rb:
MYVARIABLE = 'some string'
Accessible from anywhere in your application as MYVARIABLE
Ok, so here's what I did. Instead of just putting the value to an initializer, I've made there a simple class that handles it. The variable itself is stored in a predefined file. Besides of reading the file upon the initialization, the class updates file when the value is changed, and also re-read the file periodically to maintain consistency across workers. I've also put there some basic JSON handling and backup functionality to make life easier.
For anyone interested, here's the important code:
class Pomegranate
def initialize
#delay = 30.minutes
#path = "db/pomegranate.json"
#valid = Time.now - 1
validate
end
def get(*p)
validate
p.inject(#data) {|object,key| object[key] if object}
end
def set(*p, q, v)
hash = p.inject(#data) {|object,key| object[key]||={}}
hash[q] = v
end
def save
#valid = Time.now + #delay
File.open(#path,"w") {|f| f.write(#data.to_json)}
end
private
def validate
if #valid < Time.now
#data = ActiveSupport::JSON.decode(File.read(#path)) rescue {}
#valid = Time.now + #delay
#valid = Time.now - 1 if #data.empty?
end
end
end
$pom = Pomegranate.new
Source:
Where to put Global variables in Rails 3
Try putting it in your applicaton.rb like this:
module MyAppName
class Application < Rails::Application
YOUR_GLOBAL_VAR = "test"
end
end
Then you can call it with the namespace in your controllers, views or whatever..
MyAppName::Application::YOUR_GLOBAL_VAR
Another alternative would be using something like settingslogic. With settingslogic, you just create a yml config file and a model (Settings.rb) that points to the config file. Then you can access these settings anywhere in your rails app with:
Settings.my_setting
I've started putting constants and variables like this in the configuration object, e.g.
TestApp::Application.config.foo = 'bar'
TestApp::Application.config.something = { :a => 1, :b => 2 }

Use find to initialize a constant?

Something like this:
class Category
SOME_CATEGORY = find_by_name("some category")
end
Category::SOME_CATEGORY
tried without a problem, but want to know if it is a bad idea, and the reasons if any..
thanks
If you don't want to hit the database each time you'll have to cache the model. There are several ways to do this, but one quick way is using Memoization. This was introduced in Rails 2.2.
class Category < ActiveRecord::Base
class << self
extend ActiveSupport::Memoizable
def named(name)
find_by_name(name)
end
memoize :named
end
end
Use it like this.
Category.named("some category") # hits the database
Category.named("some category") # doesn't hit the database
The cache should stay persistent across requests. You can reset the cache by passing true as the last parameter.
Category.named("some category", true) # force hitting the database
What do you want to do?
Maybe:
class Category
def self.some_category
Category.find_by_name("some category")
end
end
So you can call:
Category.some_category
=> <Category#2....>
It's not a terrible idea, but it's not really a good one either. It doesn't really fall in line with the way Rails does things. For one thing, you'll end up with a lot of ugly constant code. Too many ALL_CAPS_WORDS and your Ruby starts to look like C++. Bleah.
For another, it's inflexible. Are you going to make one of these constants for every category? If you add a new category two months from now, will you remember to update your Rails code, add a new constant, redeploy it and restart your server?
If it's important to you to be able to access categories very easily, and not repeat DB queries, here's a bit of metaprogramming that'll automatically look them up and create static methods like Lichtamberg's for you on first access:
def self.method_missing(category, *args) # The 'self' makes this a class method
#categories ||= {}
if (#categories[category] = find_by_name(category.to_s))
class_eval "def self.#{category.to_s}; #categories[#{category}]; end"
return #categories[category]
end
super
end
With this method in place, whenever you first call Category.ham, it'll create a class method that returns the value of find_by_name("ham") -- so that neither the query nor method_missing() runs again the next time you call it. This is pretty much the way the OpenStruct class works, BTW; look it up in the Pickaxe book if you want to learn more.
(Of course you'll still have the risk that, because these are all memoized, your Rails app won't reflect any changes you make to your category objects. This makes the assumption that changes won't happen or don't really matter. It's up to you to determine whether that assumption is valid for your app. You could always put an after_update callback in your code that resets ##categories if that's a problem; but at that point this starts to get complicated.)

Resources