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
Related
I am not sure how to go about this, or if there is a better way to do this, but I have a table called leads(many) and it references agent (one).
I want to make a migration that sets a default value for the agent_id column in the leads table. But i want the default value to loop through all the agent ids. Im not sure how to do this!? Should i use a call back, or should i do in migration file?
Here is the actual question im trying to tackle:
When new leads are created assign it to an agent using a “round robin” That way new leads are distributed evenly across all the agents.
Ive attached a screenshot using SUDO code (i know its not functional as is) as to what I am thinking of doing. Any tips?
(Using ruby on rails w/ postgresql)
I think it makes sense to handle this functionality as part of the main app, and not within migration, as there seem to be a significant chunk of functionality to handle.
Probably best to handle it as part of an after_create callback in the Lead model, and use a class variable to track the next agent to be assigned as follows:
class Lead
# Assign the class variable to the first agent
##next_agent = Agent.first
after_create :set_agent
...
private
# Called by the after_create callback
# Sets the agent_id, and updates the ##next_agent class variable
def set_agent
self.agent_id = ##next_agent.id
##next_agent = find_next_agent
end
## Called from the set_agent method
## Finds the next agent based on the current value of ##next_agent
def find_next_agent
##next_agent = Agent.find(##next_agent.id + 1)
##next_agent = Agent.first unless #next_agent
end
end
The find_next_agent logic above is a simplistic example, assuming that all Agent objects have ids that increment by 1, and there are no gaps (i.e. no deletions in the table).
I'm creating an rails app, which imports some stuff from external service. At the end of that import, user should get an info about how many new items has been imported (it's like a periodical check, which adds just new items to local database. Not all of them each time.). A whole process and method-chain is quite complex so i'm looking for a best-practice on how to pass such information from deeply nested method. Schema looks more or less like that:
some_controller.rb
-> Model.method()
-> Lib1.method1()
-> Lib2.method2()
-> Lib3.method3()
-> Lib4.method4()
-> Lib5.method5()
-> items_import_method()
and i need to somehow pass info about how many new items has been imported from items_imported_method() to some_controller.rb (or any other place this import is fired). The most obvious way of doing that is passing new_items_count var all the way up though this whole method chain but it seems a bit wrong to me. Isn't there any better way?
I was thinking about some kind of internal events/messages system which would let me to subscribe to some custom channel, like activeresource events but maybe there is some well-known and suggested approach for such situation?
Thanks in advance.
One way to tackle this is to create some kind of context container object that has properties any of those steps can manipulate, then pass this in to each method on the way down.
For example:
class ContextExample
attr_accessor :example_field
end
In practice:
#context = ContextExample.new
Lib1.method1(#context)
#context.example_field
# => "Test!"
Where what you're doing is:
module Lib1
def self.method1(context)
Lib2.method2(context)
end
end
module Lib2
def self.method2(context)
context.example_field = "Test!"
end
end
These modules or classes can save context if required, and pass it on when necessary.
If you have well-defined properties you need to set and manipulate, a class with attr_accessor for those usually works pretty well. If it's a more arbitrary thing and you're not sure what might shake out in the end, either use a Hash or an OpenStruct object to capture whatever might come up.
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
What is the best way to handle a static data set (non-dynamic)?
For instance, let's say you have a model that has a set of 10 different instances, each of which is unique, but none of which will ever change throughout the lifetime of your application. It seems overkill to create an activerecord model and store this data in the database, but it seems ugly to create a generic class and store this data in the code.
What is accepted as a best practice?
Example:
You have a Rate and a User. A User can have a level from 1-10, when the level changes, the rate changes. The rate might have other information, so simply storing it as an attribute on the User might be more trouble than it's worth. Would it make sense to tie it to a Rate or to create it as a method on the User like this:
def rate
case self.level
when 1:
{ value: "foo", something: "bar", else: "baz" }
when 2:
# etc
end
end
It seems that neither of the solutions are ideal, but I'm not sure if there is something else ideal that could happen.
I would store this information in a YAML file. You could use the RailsConfig gem and create a YAML file like
level:
1:
some: value
another: value
2:
some: second value
another: second value
And then access it with
rate = 2
val = Settings.level[rate.to_s].some
(I'm not completely sure with numbers as keys in YAML, maybe you have to escape them)
I use constants in this cases: constants do not change after the declaration, but the declaration can be dynamic:
OS =
case RUBY_PLATFORM
when /linux/ then :linux
when /osx/ then :osx
when /windows/ then :windows
else :unknown
Performance should be better when using constants for static values, because they should be memoized (and because staticity should be their purpose, so probably Ruby implementations trust about it; I read something about JRuby and constants implementation, I'll post it if I'll find. EDIT I found it: http://blog.headius.com/2012/09/avoiding-hash-lookups-in-ruby.html).
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 }