I've recently started learning Ruby on Rails, and I'm trying to write a program that will allow a little bit of user interaction via the keyboard.
In the past, I've mostly only used Java, so I was wondering what Ruby's equivalent to the readKeyboard method is, and how you would use it?
My program is just a simple one that will have lists of students, courses and modules, and will allow the user to register students to a course, and enlist them on modules that are run by that course.
I have a class called 'Module', which currently looks like this:
class Module
# To change this template use File | Settings | File Templates.
##moduleScheme = nil
def initialize(v)
#val = v
end
# Set and get the #val object value
def set (v)
#val = v
end
def get
return #val
end
def addModule
moduleName = Module.new(30)
moduleRefNo = ran(100)
moduleYear(4)
end
def addModuleToScheme
moduleName.moduleScheme = schemeName
end
def removeModuleFromScheme
moduleName.moduleScheme = nil
end
def queryModule
end
end
With the 'queryModule' function, I want the user to be able to specify which module they want to query, by typing in the module's unique identifier. Presumably, I would use 'puts' to indicate that that's what the user is required to do? But then I don't know how I would read whatever the user types on the keyboard into the program. Could someone point me in the right direction?
Ruby (as far as Rails goes) is server-side, so it tends not to be directly involved in the user interaction. You'd begin to handle it on the front-end (html, javascript, etc.)
As far as the logic goes, post human-interaction, you either have that done on frontend as well (Javascript), or if you really need Ruby on Rails to utilize the interactions you occasionally perform AJAX calls (or similar) to Rails sending the relevant information.
However: if the user is directly on the console, you read the input with gets, not puts; puts will print to the command line.
Related
The Rails 'flash' message system is very useful, but I've always found the details of its implementation make its use awkward.
Specifically, the flash messages are cleared at the end of a response. This means that you have to work out whether they're going to be used in a direct page render or a redirect, then inform the flash system by using 'now' if it's not a redirect.
This seems overly complex and error-prone.
Occasionally I find myself building things that need to exhibit flash-like behaviour, and the process I use is slightly different:
class FlashlikeStore
attr_accessor :session
def initialize(session)
session[:flashlike] ||= []
self.session = session
end
def add(name, data)
self.store << { name: name, data: data }
end
def read
session.delete(:flashlike).to_json
end
def any?
store && store.any?
end
protected
def store
session[:flashlike]
end
end
With a little syntactic sugar in the application helper I can easily add my name-value pairs, and the act of reading it deletes the data. (In this case I'm actually reading this in via AJAJ, but that isn't important here.)
The upshot is that messages are only ever read once, with no need up-front to determine or guess when they're going to appear. They're read when they're needed, then they go away.
One could argue, I suppose, that a call to an object shouldn't both read data and change state, so the 'read' method could be split into a 'read' and a 'wipe' if you wanted to be purist that way. (Although you probably wouldn't be using Rails if you were.)
So the question is this: am I missing something? Is there a compelling reason for the way Rails flash messaging works, with its need for the 'now' method?
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 }
Given I've got a site where most of the resources have numerical IDs (i.e. user.id question.id etc.) but that like the Germans looking back on WWII I'd rather not reveal these to the observers, what's the best way to obfuscate them?
I presume the method is going to involve the .to_param and then some symmetric encryption algorithm but I'm not sure what's the most efficient encryption to do and how it'll impact lookup times in the DB etc.
Any advice from the road trodden would be much appreciated.
I published a Rails plugin that does this called obfuscate_id. I didn't need it to be secure, but just to make the id in the url non-obvious to the casual user. I also wanted it to look cleaner than a long hash.
It also has the advantage of needing no migrations or database changes. It's pretty simple.
Just add the gem to your Gemfile:
gem 'obfuscate_id'
And add call the obfuscate id in your model:
class Post < ActiveRecord::Base
obfuscate_id
end
This will create urls like this:
# post 7000
http://example.com/posts/5270192353
# post 7001
http://example.com/posts/7107163820
# post 7002
http://example.com/posts/3296163828
You also don't need to look up the records in any special way, ActiveRecord find just works.
Post.find(params[:id])
More information here:
https://github.com/namick/obfuscate_id
I usually use a salted Hash and store it in the DB in an indexed field. It depends on the level of security you expect, but I use one salt for all.
This method makes the creation a bit more expensive, because you are going to have an INSERT and an UPDATE, but your lookups will be quite fast.
Pseudo code:
class MyModel << ActiveRecord::Base
MY_SALT = 'some secret string'
after_create :generate_hashed_id
def to_param
self.hashed_id
end
def generate_hashed_id
self.update_attributes(:hashed_id => Digest::SHA1.hexdigest("--#{MY_SALT}--#{self.id}--"))
end
end
Now you can look up the record with MyModel.find_by_hashed_id(params[:id]) without any performance repercussions.
Here's a solution. It's the same concept as Wukerplank's answer, but there's a couple of important differences.
1) There's no need to insert the record then update it. Just set the uuid before inserting by using the before_create callback. Also note the set_uuid callback is private.
2) There's a handy library called SecureRandom. Use it! I like to use uuid's, but SecureRandom can generate other types of random numbers as well.
3) To find the record use User.find_by_uuid!(params[:id]). Notice the "!". That will raise an error if the record is not found just like User.find(params[:id]) would.
class User
before_create :set_uuid
def to_param
uuid
end
private
def set_uuid
self.uuid = SecureRandom.uuid
end
end
Hashids is a great cross-platform option.
You can try using this gem,
https://github.com/wbasmayor/masked_id
it obfuscates your id and at the same time giving each model it's own obfuscated code so all no. 1 id won't have the same hash. Also, it does not override anything on the rails side, it just provides new method so it doesn't mess up your rails if your also extending them.
Faced with a similar problem, I created a gem to handle the obfuscation of Model ids using Blowfish. This allows the creation of nice 11 character obfuscated ids on the fly. The caveat is, the id must be within 99,999,999, e.g. a max length of 8.
https://github.com/mguymon/obfuscate
To use with Rails, create an initializer in config/initializers with:
require 'obfuscate/obfuscatable'
Obfuscate.setup do |config|
config.salt = "A weak salt ..."
end
Now add to models that you want to be Obfuscatable:
class Message < ActiveRecord::Base
obfuscatable # a hash of config overrides can be passed.
end
To get the 11 character obfuscated_id, which uses the Blowfish single block encryption:
message = Message.find(1)
obfuscated = message.obfuscated_id # "NuwhZTtHnko"
clarified = message.clarify_id( obfuscated ) # "1"
Message.find_by_obfuscated_id( obfuscated )
Or obfuscate a block of text using Blowfish string encryption, allowing longer blocks of text to be obfuscated:
obfuscated = message.obfuscate( "if you use your imagination, this is a long block of text" ) # "GoxjVCCuBQgaLvttm7mXNEN9U6A_xxBjM3CYWBrsWs640PVXmkuypo7S8rBHEv_z1jP3hhFqQzlI9L1s2DTQ6FYZwfop-xlA"
clarified = message.clarify( obfuscated ) # "if you use your imagination, this is a long block of text"
Context:
In a rails 3 project I want to customise (heavily) the format and content of the "Processing" and "Completed in" log lines from ActionController. This is in order to have them match the (also custom) format of an older rails 2.3 app, allowing re-use of various analysis tools. Making them fixed-field (by use of place-holders where necessary) also makes it far easier to do ad-hoc queries on them with (say) awk, or to load them into a db or splunk without smart parsing.
I've achieved this goal quickly and heavy-handedly by forking rails and patching the LogSubscriber in question, but I am now looking to do it the right way.
Here's what I think I want to do:
Create a LogSubscriber inheriting ActionController::LogSubscriber.
Override only the start_processing and process_action methods.
Unsubscribe the original ActionController::LogSubscriber, which (sadly) registers itself as soon as the class is loaded. This is the step I'm stuck on.
Attach my custom subscriber to :action_controller, using attach_to.
I note that things would be easier if the hook-up of the default ActionController's log subscriber were done as a config step, rather than on class load.
Question:
Assuming the approach outlined above is suitable, how can I unsubscribe or unattach the default log subscriber ActionController::LogSubscriber? The base class, ActiveSupport::LogSubscriber provides attach_to but no means of detaching.
Alternatively, what is the cleanest way of altering the behaviour of (or entirely suppressing) the two methods (start_processing, process_action) in the above mentioned class, ActionController::LogSubscriber.
Of course, I'd also welcome any other (maintainable) approach which provides full freedom in customizing the log format of the two lines mentioned.
Unsuitable approaches:
My problem cannot be solved by adding instrumentation, as documented nicely by mnutt.
I'd like to avoid monkey-patching, or hacking in a way that may break when the rails implementation (not interface) changes.
References:
Docs and Source for ActiveSupport::LogSubscriber
Source for ActionController::LogSubscriber
Docs, Source, and RailsCast for ActiveSupport::Notifications
I have worked out the following solution.
I'm not particularly happy with aspects of it (for example, having to unsubscribe ALL interest in 'process_action.action_controller'), and I'll certainly accept a better answer.
We add the following as config/initializers/custom_ac_log_subscriber.rb
module MyApp
class CustomAcLogSubscriber < ActiveSupport::LogSubscriber
INTERNAL_PARAMS = ActionController::LogSubscriber::INTERNAL_PARAMS
#Do your custom stuff here. (The following is much simplified).
def start_processing(event)
payload = event.payload
params = payload[:params].except(*INTERNAL_PARAMS)
info "Processing #{payload[:controller]}##{payload[:action]} (...)"
info " Parameters: #{params.inspect}" unless params.empty?
end
def process_action(event)
payload = event.payload
message = "Completed in %.0fms [#{payload[:path]}]" % event.duration
info(message)
end
def logger
ActionController::Base.logger
end
end
end
#Prevent ActionController::LogSubscriber from also acting on these notifications
#Note that the following undesireably unregisters *everyone's*
#interest in these. We can't target one LogSubscriber, it seems(?)
%w(process_action start_processing).each do |evt|
ActiveSupport::Notifications.unsubscribe "#{evt}.action_controller"
end
#Register our own interest
MyApp::CustomAcLogSubscriber.attach_to :action_controller
Notes on aborted initial approach:
I initially attempted an approach of unregistering the default LogSubscriber, with the intention that our custom one would inherit and do all the work (without having to reimpl).
However, there are a number of problems.
1) It's not enough to remove the LogSubscriber as follows:
ActiveSupport::LogSubscriber.log_subscribers.delete_if{ |ls|
ls.instance_of? ActionController::LogSubscriber }
As the request for notifications remains registered.
2) Further, when you inherit a LogSubscriber and register it, it appears any non-overridden methods will not be called using the inherited implementation.
For unsubscribe I found this solution: (Rails 3.0.10):
notifier = ActiveSupport::Notifications.notifier
subscribers = notifier.listeners_for("sql.active_record")
subscribers.each {|s| ActiveSupport::Notifications.unsubscribe s }
For anyone else searching for this, Mathias Meyer has a nice writeup
Also, he has a gem that can be a good example for how to unsubscribe existing subscribers and substitute one's own.
If you want to unsubscribe a patter matching a regex you can do this:
ActiveSupport::Subscriber.subscribers.flat_map(&:patterns).grep(/sql/).map \
&ActiveSupport::Notifications.method(:unsubscribe)
Add a file in config/initializers/filter_logger.rb
# required this file
require 'action_controller/log_subscriber'
module MyApp
class MyLogSubscriber < ActiveSupport::LogSubscriber
INTERNAL_PARAMS = %w(controller action format _method only_path)
def start_processing(event)
payload = event.payload
params = payload[:params].except(*INTERNAL_PARAMS)
info "Processing #{payload[:controller]}##{payload[:action]} (...)"
info " Parameters: #{params.inspect}" unless params.empty?
end
def process_action(event)
payload = event.payload
message = "Completed in %.0fms [#{payload[:path]}]" % event.duration
info(message)
end
end
end
%w(process_action start_processing).each do |evt|
ActiveSupport::Notifications.unsubscribe "#{evt}.action_controller"
end
MyApp::MyLogSubscriber.attach_to :action_controller
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.)