Rails tutorial port to amazon dynamodb - ruby-on-rails

I'm following michael hartl's tutorial for getting started with rails...
http://ruby.railstutorial.org/
I'm trying to modify my database backend from PostgreSQL to Amazon dynamodb.. Any advice/help on how to achieve this would be very helpful.

This is going to be tough because Hartl's tutorial stresses learning ActiveRecord knowledge very strongly, whose core components don't really make sense in the context of DynamoDB.
If you're insistent, Amazon provides the aws-sdk gem that you can bundle into your application. Set up your appropriate credentials in the config/initializers/aws.rb initializer
AWS.config({
:access_key_id => 'your_access_key_id',
:secret_access_key => 'your_secret_access_key',
})
You'll probably want your models (where appropriate) to inherit from AWS::Record::HashModel if you want to have a sensible way of interfacing with objects.
You'll also probably want to write some assessors in this format.
def img= image
self.has_image = true
AWS::S3.new.buckets[:images_cloudstock].objects[id].write(image.read)
end
def img
AWS::S3.new.buckets[:images_cloudstock].objects[id].url_for(:read) if has_image
end

Related

RoR, Fog: Getting Started with Fog

I just installed the gem asset_sync and I am trying to get set up with my AWS account. When I run bundle exec rake assets:precompile I get the following errror:
AssetSync::Config::Invalid: Fog provider can't be blank, Fog directory can't be blank
I understand the simply reason that I am getting this error, namely that I havent pushed the Fog provider or directory to heroku. What I am stumped about is where to put the Following code (Taken from the Fog README). In config/initializers/fog.rb? Is this all I need to do to start using fog, other than installing the gem?
require 'rubygems'
require 'fog'
# create a connection
connection = Fog::Storage.new({
:provider => 'AWS',
:aws_access_key_id => YOUR_AWS_ACCESS_KEY_ID,
:aws_secret_access_key => YOUR_AWS_SECRET_ACCESS_KEY
})
# First, a place to contain the glorious details
directory = connection.directories.create(
:key => "fog-demo-#{Time.now.to_i}", # globally unique name
:public => true
)
not a problem, getting started tends to be the hardest part.
The answer is, it depends. I'd actually venture to say it would be best to put this in your environment based initializers, ie config/init/development or config/init/production, etc. Relatedly, you probably will not want to generate a new directory every time you start your app (there is an account level limit of 100 total I believe). So you might want to either set a key for each environment for that create or simply create the directory somewhere outside the initializers (and within the initializer you can assume it exists).
If you want to use that directory directly, you'll still need to create a reference, but you can create a local reference without making any api calls with #new like this:
directory = connection.directories.new(:key => ...)
As for asset_sync, it needs those keys and a reference to the directory key, which you will probably want to provide via ENV vars (to avoid checking your credentials into version control). You can find details on which keys and how to set them here: https://github.com/rumblelabs/asset_sync#built-in-initializer-environment-variables (the readme also describes how to do it via initializers, but that probably isn't the best plan).
Hope that helps!

How do I reference a tiny_tds connection in ruby/sinatra via database.yml (or other)?

I'm trying to figure out how to pull data from a database without the need to place a connection string at the top of each ruby file.
I'm learning the basics of ruby via a little Sinatra app I'm putting together which pulls data from a MSSQL database.
So far I've managed to create various simple erb pages that display data from the MSSQL database by using the following code structure at the top of each file:-
<% client = TinyTds::Client.new(:username => 'name', :password => 'password', :dataserver => 'hostname', :database => 'database') %>
<% data = client.execute("SELECT * from tablename") %>
From the books, guides and online tutorials I've found based on lots of configs to do with PostgreSQL or MySQL databases it seems to me I need to be creating a central file to store my connection data (such as a database.yml file) and then referencing that somewhere/somehow in my app.
Would that be correct, and should I be doing that in my main.rb file so that each of my .erb files do not require the connection string or do I have to still refer to the database in each .erb file also?
I've noted references to creating database config variables such as:-
db_config = YAML.load(File.Open("/path_to_file/database.yml")) [ENV['RAILS_ENV']]
but that clearly seems suited to Rails apps.
Could I do something similar for my sinatra driven 'app'?
Thanks.
This should work:
require "sinatra"
require "sinatra/config_file"
config_file 'path/to/config.yml'
DB = TinyTds::Client.new(
:username => settings.name,
:password => settings.password,
:dataserver => settings.hostname,
:database => settings.database
)
get '/' do
#result = DB.do_stuff
haml :index
end
What I would suggest though, is that you look for an ORM that supports TinyTDS and use that to set up the database connection and run queries. I use Sequel a lot and I know it supports TinyTDS, but I'm sure others do too.
I'd also suggest not putting things like database settings in a file that gets checked in to source control, as it's sensitive information. I prefer to put this kind of thing into environment variables for production, and use a config file to load the environment variables in development (for convenience). Using the above example:
DB = TinyTds::Client.new(
:username => ENV["name"],
:password => ENV["password"],
:dataserver => ENV["hostname"],
:database => ENV["database"]
)
Those env variables are loaded into the production server's memory, which makes them a little bit more secure. For development, I load YAML files before starting up Sinatra, adding each value to an ENV var. I'm not suggesting this is a standard way, but it's how I do it.

Declaring In-Line Secret Keys in Ruby On Rails

I'm learning Ruby On Rails. I'm trying to use the aws-s3 gem to access Amazon S3. One line of my controller code reads:
AWS::S3::Base.establish_connection!(
:access_key_id => 'myrealaccesskeyishere',
:secret_access_key => 'myrealsecretkeyishere'
)
I've noticed that if I make an error, sometimes rails will come back and show a few lines of code where it thinks the error might be. Should I not be writing these out in the .rb controller files like this? Am I potentially risking my secret key? If so, how should I be doing this instead?
You should put this in an initializer. Place it in config/intializers/amazon_s3.rb
Is there a reason you are putting this code directly in the controller?

moving files from virtual server to cloud , in rails

I have a ruby on rails project
I have some few thousand files associated with the site.
Is there a simple way to move all the files from virtual host to cloud (amazon).
Basically I have a lot of images and I am using paperclip gem to store the images.There are already a few users and have thousands of images . Now I have planned to migrate to cloud(amazon). I have used aws-s3 gem for this purpose and it works fine for new images.
But How do I put the old images which were already uploaded ?
Do I have to manually zip and reupload ? or is there a better way ?
Thank you
Hi I would right a simple rake task to accomplish this. It would look like something like this (not tested):
desc "port files to s3"
task :port => :environment do
AWS::S3::Base.establish_connection!(
:access_key_id => S3_CONFIG['access_key_id'],
:secret_access_key => S3_CONFIG['secret_access_key']
)
Images.all.each do |image|
new_image_path = "/images/#{image.id}/#{image.file_name_with_extension}"
AWS::S3::Object.store(new_image_path,open(image.current_path_to_image),S3_CONFIG['bucket_name'],:access => :public_read)
end
end
A few notes:
if you are using paperclip to manage and access the files you will want to store them in s3 so paperclip can access them. To do that use Paperclip::Interpolations.interpolate method. like:
new_image_path = Paperclip::Interpolations.interpolate("/accounts/:account_id/images/:id/:style/:basename.:extension",image,:small)
And this goes without saying -- make sure you test this in a staging environment!
Good luck.

Rails - Paper_Clip - Support for Multi File Uploads

I have paper_clip installed on my Rails 3 app, and can upload a file - wow that was fun and easy!
Challenge now is, allowing a user to upload multiple objects.
Whether it be clicking select fileS and being able to select more than one. Or clicking a more button and getting another file upload button.
I can't find any tutorials or gems to support this out of the box. Shocking I know...
Any suggestions or solutions. Seems like a common need?
Thanks
Okay, this is a complex one but it is doable. Here's how I got it to work.
On the client side I used http://github.com/valums/file-uploader, a javascript library which allows multiple file uploads with progress-bar and drag-and-drop support. It's well supported, highly configurable and the basic implementation is simple:
In the view:
<div id='file-uploader'><noscript><p>Please Enable JavaScript to use the file uploader</p></noscript></div>
In the js:
var uploader = new qq.FileUploader({
element: $('#file-uploader')[0],
action: 'files/upload',
onComplete: function(id, fileName, responseJSON){
// callback
}
});
When handed files, FileUploader posts them to the server as an XHR request where the POST body is the raw file data while the headers and filename are passed in the URL string (this is the only way to upload a file asyncronously via javascript).
This is where it gets complicated, since Paperclip has no idea what to do with these raw requests, you have to catch and convert them back to standard files (preferably before they hit your Rails app), so that Paperclip can work it's magic. This is done with some Rack Middleware which creates a new Tempfile (remember: Heroku is read only):
# Embarrassing note: This code was adapted from an example I found somewhere online
# if you recoginize any of it please let me know so I pass credit.
module Rack
class RawFileStubber
def initialize(app, path=/files\/upload/) # change for your route, careful.
#app, #path = app, path
end
def call(env)
if env["PATH_INFO"] =~ #path
convert_and_pass_on(env)
end
#app.call(env)
end
def convert_and_pass_on(env)
tempfile = env['rack.input'].to_tempfile
fake_file = {
:filename => env['HTTP_X_FILE_NAME'],
:type => content_type(env['HTTP_X_FILE_NAME']),
:tempfile => tempfile
}
env['rack.request.form_input'] = env['rack.input']
env['rack.request.form_hash'] ||= {}
env['rack.request.query_hash'] ||= {}
env['rack.request.form_hash']['file'] = fake_file
env['rack.request.query_hash']['file'] = fake_file
if query_params = env['HTTP_X_QUERY_PARAMS']
require 'json'
params = JSON.parse(query_params)
env['rack.request.form_hash'].merge!(params)
env['rack.request.query_hash'].merge!(params)
end
end
def content_type(filename)
case type = (filename.to_s.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
when %r"jp(e|g|eg)" then "image/jpeg"
when %r"tiff?" then "image/tiff"
when %r"png", "gif", "bmp" then "image/#{type}"
when "txt" then "text/plain"
when %r"html?" then "text/html"
when "js" then "application/js"
when "csv", "xml", "css" then "text/#{type}"
else 'application/octet-stream'
end
end
end
end
Later, in application.rb:
config.middleware.use 'Rack::RawFileStubber'
Then in the controller:
def upload
#foo = modelWithPaperclip.create({ :img => params[:file] })
end
This works reliably, though it can be a slow process when uploading a lot of files simultaneously.
DISCLAIMER
This was implemented for a project with a single, known & trusted back-end user. It almost certainly has some serious performance implications for a high traffic Heroku app and I have not fire tested it for security. That said, it definitely works.
The method Ryan Bigg recommends is here:
https://github.com/rails3book/ticketee/commit/cd8b466e2ee86733e9b26c6c9015d4b811d88169
https://github.com/rails3book/ticketee/commit/982ddf6241a78a9e6547e16af29086627d9e72d2
The file-uploader recommendation by Daniel Mendel is really great. It's a seriously awesome user experience, like Gmail drag-and-drop uploads. Someone wrote a blog post about how to wire it up with a rails app using the rack-raw-upload middleware, if you're interested in an up-to-date middleware component.
http://pogodan.com/blog/2011/03/28/rails-html5-drag-drop-multi-file-upload
https://github.com/newbamboo/rack-raw-upload
http://marc-bowes.com/2011/08/17/drag-n-drop-upload.html
There's also another plugin that's been updated more recently which may be useful
jQuery-File-Upload
Rails setup instructions
Rails setup instructions for multiples
And another one (Included for completeness. I haven't investigated this one.)
PlUpload
plupload-rails3
These questions are highly related
Drag-and-drop file upload in Google Chrome/Chromium and Safari?
jQuery Upload Progress and AJAX file upload
I cover this in Rails 3 in Action's Chapter 8. I don't cover uploading to S3 or resizing images however.
Recommending you buy it based solely on it fixing this one problem may sound a little biased, but I can just about guarantee you that it'll answer other questions you have down the line. It has a Behaviour Driven Development approach as one of the main themes, introducing you to Rails features during the development of an application. This shows you not only how you can build an application, but also make it maintainable.
As for the resizing of images after they've been uploaded, Paperclip's got pretty good documentation on that. I'd recommend having a read and then asking another question on SO if you don't understand any of the options / methods.
And as for S3 uploading, you can do this:
has_attached_file :photo, :styles => { ... }, :storage => :s3
You'd need to configure Paperclip::Storage::S3 with your S3 details to set it up, and again Paperclip's got some pretty awesome documentation for this.
Good luck!

Resources