The flash is a special part of the session which is cleared with each request.
This means that values stored there will only be available for the request,
which is useful for storing error messages, notices, etc.
Note: this does not work for redirect requests.
If you set a flash message and then you redirect to another path, the flash hash
will be cleared first and then the redirect request will happen, with a clean flash hash.
usage:
123
useRack::Session::Flashenv['rack.session']['flash'][:error]='Something bad just happened.'
If you are using Cuba, you can do:
123
Cuba.useRack::Session::Flashsession['flash'][:error]='Something bad just happened.'
or even better, you can create a plugin
12345678910
moduleFlashHelperdefflashsession['flash']endendCuba.useRack::Session::FlashCuba.pluginFlashHelperflash[:error]="Something bad just happened."
Here is the implementation of the rack middleware.
123456789101112131415161718192021222324252627
moduleRackmoduleSessionclassFlashdefinitialize(app)@app=appenddefcall(env)dup.call!(env)enddefcall!(env)env['rack.session']||raise(RuntimeError,'You\'re missing a session handler. '+'You can get started using Rack::Session::Cookie')env['rack.session']['flash']||={}response=@app.call(env)env['rack.session']['flash'].clearresponseendendendend
Cuba: is a Ruby microframework for web development. Sprockets: is a Ruby library for compiling and serving web assets.
If you never heard of sprockets before you should know that is the heart of the Rails Asset Pipeline.
We can also use it outside of rails to build an asset pipeline for our Cuba applications.
We will need an instance of the Sprockets::Environment class to access and serve assets
from our application. The Sprockets Environment has methods for retrieving and serving assets,
manipulating the load path, and registering processors. It is also a Rack application that
can be mounted at a URL to serve assets over HTTP, we will do this in our config.ru file.
After creating the sprockets environment we need to configure the paths from where
we will be serving the assets. For example if you have the following folder structure
in your project:
1234
assets-javascripts-stylesheets-images
To configure the paths we append them to the sprockets environment we instantiated:
Here is a full sample using the Yui Compressor for compressing the stylesheets and
the javascripts. I am also using memcache to cache the assets to avoid resending them
to the client browser if they had not changed.
config.ru
123456789101112131415161718192021222324252627
requireFile.expand_path("app",File.dirname(__FILE__))require'yui/compressor'require'sprockets'require'dalli'# GZip compessionuseRack::Deflatermap'/assets'doenv=Sprockets::Environment.newenv.append_path'assets/javascripts'env.append_path'assets/stylesheets'env.append_path'assets/images'# use MemCacheenv.cache=Dalli::Client.new('localhost:11211')# Compress assetsenv.js_compressor=YUI::JavaScriptCompressor.newenv.css_compressor=YUI::CssCompressor.newrunenvendmap'/'dorunCubaend
Cuba: is a Ruby microframework for web development. CoffeeScript: is a little language that compiles into JavaScript.
Now that we have set the basics, we can continue with the topic that bring us
here today. In order to serve coffeescript as compiled javascript we will need to
use the Rack::Coffee middleware.
In your Cuba app, this is as easy as doing the following:
1234
require"rack/coffee"# Search the coffeescript files int /public/coffeeCuba.useRack::Coffee,root:"public",urls:["/coffee"]
Then you just need to include the coffeescript file in your view,
you will need to replace the .coffee extension with .js.
12
<!-- This will serve /public/coffee/script.coffee --><script src="/public/coffee/script.js"></script>
Troubleshooting
If you are using the Rack::Static middleware, you will need to serve your
coffeescript files from a different location.
Today I had a situation where I had to translate a geopoint (latitude, longitude)
into a physical address, commonly called reverse geocode. Think on a Place model,
where each new record have a latitude and longitude, and you need to resolve that
geopoint into a real world address and save it on the record.
For the reverse geocode part I used the OpenStreetMap Nominatim service, with the osmn ruby gem.
I first had a before_save model hook to set the address, doing a reverse geocode
of the latitude and longitude using the osmn library, but, as you can imagine,
I had to wait for the nominatim service to respond to be able to save the record.
The process was taking to much time, and I actually didn’t need the address
value right away after creating the record.
Moving it to an after_create model hook didn’t work neiter because the code still
waits for all the hooks to end before returning. I first thought that I would need
delayed_job or some kind of background worker that could do the reverse geocode in
the background after creating the record, but I didn’t had anything of that in
the project and it sounded as an overkill solution.
Then I thought of Threads. A simple and plain thread will be the perfect solution
in this case. Inside the after_create method I created a Thread with the reverse geocode call.
That way I can create the record, and continue processing new records while the
reverse geocode is ocurring in a separated thread.
Ok, we saw public-key (or asymmetric) and symmetric-key cryptography. With the
first one you have a pair of keys, one of which is public and you can share with
your friends in order to exchange info, they can encrypt the data using your
public key and only you will be able to decrypt it. Also you can encrypt (sign in)
something with your private key, and that can only be decrypt using your public
key, which ensures the data came from you (that’s why is called signature).
So, if you encrypts the data with your private-key, and then with your friend’s
public key, you will have a safe channel, the data will be signed by you and only
your friend will be able to see it. Unfortunately this can only be used with small
amounts of data (limited by the size of the key).
On the other side, we have the symmetric-key encryption, that can encrypt large
amounts of data without problem, but we cannot send the secret-key by email to
our friends, since it could be intercepted or revealed and all will be able to
decrypt the data. If only there was a way to share this small secret-key with
your friend… oh wait.
Using the public-key encryption to share our symmetric-key, and then the
symmetric-key to encrypt the data. Finally you send the encrypted data with
the encrypted and signed symmetric key.
require'openssl'# This is our secretdata=File.read('a-lot-of-secrets.txt')# Create a RSA key (public-key) of 2048 bitsrsa_key=OpenSSL::PKey::RSA.new(2048)# Extract the public keypublic_key=rsa_key.public_key.export# Send my public key to my friend# Get his public key and store itfriend_key=share_public_key(public_key)# Create AES cipher, (symmetric-key) of 128 bitsaes_cipher=OpenSSL::Cipher::AES.new(128,'CBC')aes_cipher.encryptkey=aes_cipher.random_keyiv=aes_cipher.random_iv# sign the keysigned_key=rsa_key.private_encrypt(key)signed_iv=rsa_key.private_encrypt(iv)# encrypt keys with friend's keyfriends_rsa_key=OpenSSL::PKey::RSA.new(friend_key)encrypted_key=friends_rsa_key.public_encrypt(signed_key)encrypted_iv=friends_rsa_key.public_encrypt(signed_iv)# send to friend the encrypted_key + encrypted_iv# He will decrypt first with his private key, and then with our public key.# now that we securely shared the symmetric key encryption, # we can send him the encrypted dataencrypted_data=aes_cipher.update(data)+aes_cipher.final
We already saw how to encrypt small amounts of data, like credit cards, web forms
or signatures with
public-key encryption. Today we will see how to encrypt large amounts of data using
symmetric-key encryption.
Symmetric-key algorithms are a class of algorithms for cryptography that use the
same cryptographic key for both operations, encryption and decryption of data,
for this reason the key must remain secret.
OpenSSL supports a large variety of symmetric-key algorithms, a list of the
supported algorithms can be obtained with:
1
OpenSSL::Cipher.ciphers
To create a Cipher instance you need to know 3 things, the name of the algorithm,
the length of the key in bits and the cipher mode to be used. The most generic way to create a new Cipher object is like this:
The string parameter must be all in uppercase or all in lowercase.
Each algorithm has also a class defined under the Cipher namespace that you can
use to obtain an instance of that algorithm:
1
cipher=OpenSSL::Cipher::AES.new(128,'CBC')
For symmetric algorithms, encryption and decryption are very similar operations,
and you can do both with your Cipher instance. However you need to set the instance
with the operation you intend to do with it. This is done calling encrypt or decrypt
on the instance without parameters:
After obtaining the instance of the Cipher and setting the operation, we need to
set the key to be used. The cipher instance provides a way to generate a random
and more secure key than a plain and simple password. The cipher modes CBC, CFB,
OFB and CTR all need an initialization vector, IV for short, if not explicity set,
OpenSSL will default the IV to all-zeroez. The cipher instance also provides a way
to create a secure random IV. In brief, you will need a key and an initialization vector:
ECB and CBC are both block-based modes, unlike the streaming-based modes, they operate on fixed-size blocks of data, and therefore they require a finalization (padding) to decrypt the last block of data. For that reason you need to add the content of #final to the encryption/decryption buffer or you will end up with errors or truncated data.
Finally, all said, we can encrypt and decrypt some data:
1234567891011121314
plain_data="My secret is that there is no secret"cipher=OpenSSL::Cipher::AES.new(128,'CBC')cipher.encryptkey=cipher.random_keyiv=cipher.random_ivencrypted_data=cipher.update(plain_data)+cipher.finalcipher.decryptcipher.key=keycipher.iv=ivplain_data=cipher.update(encrypted_data)+cipher.final
Hi, today’s tip is about protecting data with encryption, we will use public-key cryptography, this allow us to encrypt data using a key public, that you can give
your friends or to your mother or to your clients. They can encrypt the data that
they want to send you or you want to keep secure, and only you can decrypt it with
your private key, the private key must remain in secret to yourself and you can
protect it with a passphrase.
Warning
The method that will be described here only allows the encryption of a small amount of data. To be precise, you cannot encrypt anything larger than the key size minus 11 bytes of padding. If you will use a 2048 bit key size, you will be able to encrypt upto (2048 bits / 8) bytes – 11 bytes = 245 bytes of data.
You can use this, for example, to encrypt data in a web form, and store them
secured. So you can provide the client side with a public key, and later only
you will be able to retrieve or decrypt that data on the server side using the
private key. This can be usefull to capture and send sensitive data through the
internet.
Ok, to do this, we first need to generate our keys. We will use openssl because
I had a bad experience using gpgme on ruby, especially when I needed it to encrypt
a large number of items, the gpg context was crashing without any explanation.
I reported the bug to gpgme.
The private key is protected by a password, you can provide your own password or
generate a random one with ruby:
To generate the keys with openssl we will use the unix command line:
1234567891011
$ openssl genrsa -des3 -out private-key.pem 2048
Generating RSA private key, 2048 bit long modulus
............+++
............+++
Enter pass phrase for private-key.pem:
Verifying - Enter pass phrase for private-key.pem:
Now we will extract the public key from the private key:
$ openssl rsa -in private-key.pem -out public-key.pem -outform PEM -pubout
Enter pass phrase for private-key.pem:
writing RSA key
Now that we have the keys, we can encrypt data with the following ruby code:
1234567
require'openssl'public_key=File.read('public-key.pem')plain_data='My secret is the color blue'key=OpenSSL::PKey::RSA.new(public_key)encrypted_data=key.public_encrypt(plain_data)
The encrypted_data is binary data, so if you want to send in the body of an
email or as part of a json request, you will need to convert it to a Base64 string:
As we mentioned above, this will only allow us to encrypt small amounts of data,
to encrypt more data we will need symmetric-key encryption. You can then use the
public-key encryption to protect your symmetric-key.
We will see symmetric-key encryption with ruby in another post.
Hi, today’s tip is about how to ordinalize numbers in current locale, ActiveSupport by default ordinalize the numbers in english, if we want to
translate these values to the current locale language we may need to extend ActiveSupport.
After googling a little I found this post that solves the issue, here is my implementation based on that approach.
The ordinalize method is implemented on the ActiveSupport::Inflector module,
so we will override that method, Rails provides a place where you can put these
overrides, in config/initializers/inflections.rb. Since it is a script
executed during the initialization of Rails, you will need to restart the server
to use this method.
Here is the config/initializer/inflections.rb file:
12345678910111213141516171819202122232425
moduleActiveSupportmoduleInflectordefordinalize(number)abs_number=number.to_i.absrules=I18n.t'number.ordinals',:default=>""# Assume English for compatrules={:'\A\d{0,}(11|12|13)\z'=>"%dth",:'\A\d{0,}1\z'=>"%dst",:'\A\d{0,}2\z'=>"%dnd",:'\A\d{0,}3\z'=>"%drd",:other=>"%dth"}ifrules.empty?match=rules.finddo|rule|Regexp.new(rule[0].to_s).match(abs_number.to_s)endmatch=match&&match[1]||rules[:other]match%numberendendend
After adding the override, if we try to ordinalize numbers in other locales we
will still get the default english ordinal value. To make our code work, we need
to specify how to ordinalize the numbers in other locales, this is done in the
config/locales/*.yml files.
For example, to ordinalize numbers in Spanish(Argentina), we modify the file
config/locales/es-AR.yml.