James Johnson

RSS
May 1

Ridiculously Easy File-Sharing: Let's Crate: Crate+

letscrate:

Over the past few months, our team has been judiciously testing a new, faster, speedier, and more cost-efficient backend for the infrastructure that powers Crate.

In the process of cleaning up the code behind our web application (both backend and frontend), we have made the entire site faster and…

Hooking Up My Rails App with Amazon s3 Using Paperclip

For quite some time I have been meaning to figure out how to get my static content onto Amazon s3. Using the heroku help here it wasn’t too bad at all! This is how I did it:

First go to Amazon’s AWS page here and sign up for s3. You then need to create a bucket, and it needs to be unique across the whole of Amazons site. As you can see, my bucket is called media.domainsby.me. It does not need to be reliant on a domain name, but I figured there would be a good shot it was unique.

Next I added the gems ‘paperclip’ and ‘aws-s3’ to my Gemfile and ran bundle install.

Then I modified my model to reflect the storage place for my application. If you notice, I am naming the image that I am uploading “background” and I am referencing a s3.yml file that has my credentials.


//This is my model 
class User < ActiveRecord::Base

has_attached_file :background,
                :storage => :s3,
                :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
                :path => "/:style/:filename"
end

After you add this to the model, you need to run a migration in order to tell you application information about the image. If you are using heroku, you still need to run this migration. I forgot this step at first and while I was able to save images - my application did not know the names of those files.


class AddBackgroundToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :background_file_name, :string
    add_column :users, :background_content_type, :string 
    add_column :users, :background_file_size, :integer
  end

  def self.down
    remove_column :users, :background_file_name
    remove_column :users, :background_content_type
    remove_column :users, :background_file_size
  end
end
Then Run:

rake db:migrate
Now add the s3.yml file under config/s3.yml

//here is the s3.yml file

development:
  bucket: media.domainsby.me
  access_key_id: ACCESS_KEY
  secret_access_key: SECRET_ACCESS_KEY

test:
  bucket: media.domainsby.me
  access_key_id: ACCESS_KEY
  secret_access_key: SECRET_ACCESS_KEY

production:
  bucket: media.domainsby.me
  access_key_id: ACCESS_KEY
  secret_access_key: SECRET_ACCESS_KEY

Then finally, I put in the necessary information to both save and retrieve images - On my form I put this code for uploading images on the site:


<%= form_for @user, :html => { :multipart => true } do |f| %>
<%= f.file_field :background, :class => "inputshome" %>
<% end %>

And then in my show view in order to retrieve the image from Amazon


<%= image_tag @user.background.url %> 

Its that easy! If you are having trouble, jump into your console and make sure that the User model knows where the image is e.g. run User.last.background.url and see if it returns anything. In order to test if the image is being saved correctly, you can log into your amazon account and check if it is there.

Let me know if you have any questions!

Username as slug / domain URL in Rails 3

I am currently working on a rails app that has user profile pages, and I wanted to use those usernames as the domain slug. Meaning I want it to look like www.exampledomain.com/jamesjohnson. This was actually pretty easy to do and really just required a small modification to the config/routes.rb file.

In my user model, one of my columns is username and so that is what I used. For the profile page, I figured the show action would make the most sense as the controller/html file to use. Here is the code:


// config/routes.rb
resources :users
  match ":username" => 'users#show', :as => :username
Controller Code

def show
    @user = User.find_by_username(params[:username])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @user }
    end
  end

My First “Real” Rails App - Amazon Product API with JQuery Autocomplete UI

Today I finally finished the two month long saga of my most recent rails app. The app is called TheBookGnome (www.thebookgnome.com) and it is very simply a book voting website.

Users come to TheBookGnome.com - vote for books they like, add books they don’t see, and check out books posted by others. There is a little bit of a social aspect, with the concept of “karma” (haha this was a feature I added because I could), but for the most part its about the books.

The coolest part about building this, is that it covered some of the important stuff that goes into lots of web apps.
  1. Autocomplete (Used J-Query UI - Pasting my code below!)
  2. Authentication (Used Authlogic)
  3. Voting System (Vote_fu via the Thumbs_up gem)
  4. Basic intro to GIMP
  5. MUCH more comfortable with HTML and CSS.

In looking back on this project the biggest mistake was not having a plan. I was so excited to start building that I put no real effort into how I wanted the site to end up. This resulted in me wasting a TON of time and probably resulted in a worse finished product.

I think I am starting to learn why the “idea” is the least important part most of the time when it comes to small side projects like this… In order to include a bit of code, here are the main parts of my Autocomplete/Amazon API tie in. I used the JQuery Autocomplete UI and JSON format, if you have any questions let me know!

In my model I am making a call to the Amazon API and then returning the response in a JSON format. The JSON format is important for the autocomplete. JQuery has to be able to read the response you are sending, so formating the response like this, stops me from having to do it later in my controller or view.


// amazon.rb (model)
 def self.amazon(search)
     keywords = "#{search}"
     resp = Amazon::AWS.item_search('Books', { 'Title' => keywords })
     items = resp.item_search_response[0].items[0].item
        a = items[0]
        b = items[1]
        c = items[2]
        [{:label => "#{a.item_attributes.title.to_s[0,85] unless a.item_attributes.title.nil?}",
                     :value => "#{a.item_attributes.title.to_s unless a.item_attributes.title.nil?}",
                     :img => "#{a.small_image.url.to_s unless a.small_image.nil?}"""
                    },

        {:label => "#{b.item_attributes.title.to_s[0,85] unless b.item_attributes.title.nil?}",
                     :value => "#{b.item_attributes.title.to_s unless b.item_attributes.title.nil?}",
                     :img => "#{b.small_image.url.to_s unless b.small_image.nil?}"""
                    },
        {:label => "#{c.item_attributes.title.to_s[0,85] unless c.item_attributes.title.nil?}",
                     :value => "#{c.item_attributes.title.to_s unless c.item_attributes.title.nil?}",
                     :img => "#{c.small_image_url.to_s unless c.small_image.nil?}"""
                    }]

   end

My controller is pretty straight forward - it is calling the method that I defined in the model. The view is also pretty straight forward - just formatting to_json.html_safe. The last part is important and took me a while to figure out. The javascript was not being escaped correctly and this method fixed it up for me.


//amazon_controller.rb
    def index
        @amazon = Book.amazon(params[:term])

        respond_to do |format|
        format.html
        format.js
         end
  end

// index.js.erb

<%= @amazon.to_json.html_safe %>

Finally, my JS I ended up modifying an example on the JQuery UI documentation page. As you can see, I am appending both the image and the label to a list, which then with my css, I formatted to show correctly. Datatype here is JSON, as i mentioned earlier, and the url: amazon.js is what ends up getting returned to JQuery to format as autocomplete. If you visit the url /amazon.js - you should see your data being returned there in JSON format, if not, then the issue is not with the autocomplete code, but with the way you are accessing the data.

//javascript/application.js
$(document).ready(function(){
$(".auto_search_complete").autocomplete({
dataType: "json",
source: '/amazon.js',
minLength: 3
}).data( "autocomplete" )._renderItem = function(ul, item) {
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a>" + "<img src='" + item.img +" '>" + item.label + "<a/>" )
.appendTo( ul );} })

Nothing special about the form, I did it in HAML because I was following along another example that did it that way, but I don’t think it ended up making a difference. As you can see, the :term gets passed to the query, which then ends up being what is used to search Amazon. The classes and ids are used by JQuery in order to execute the autocomplete code, and also by the CSS in order to modify the forms appearance.


// new.html.haml 
  - form_tag '/amazon/index', :method => 'get', :id => 'search_form', :class => 'search' do
     %p
        = text_field_tag :term, params[:term], :class => "auto_search_complete"
        = submit_tag "add books", :name => nil, :class => 'button', :id => "search_btn"

Let me know if you have any questions!

Do you know anything about PHP? If not, why did you choose ruby-on-rails over PHP?

I don’t know much about PHP, except that the frameworks do not seem as “popular”. I put that in quotes because I don’t really know, its just the perception that I get from reading about others experiences and advice from friends. I know that symfony is supposedly a pretty cool, but I have never used it.

My goals are very much focused on web applications, and ruby on rails seems to be one of the best tools for doing that quickly. Especially with Heroku, it means there is a whole lot less that you need to learn to get started.

I'm in EXACTLY the same position as you are with learning RoR. I'm the Art Director at Blind Acre Media and feel to better communicate with the dev team, I should be able to speak their language. Now, my question for you is, did you start by learning Ruby basics before you jumped into Rails? If so, when did you feel you learned enough Ruby to feel safe moving on to Rails?

Anonymous

I still don’t really know a whole lot of ruby and it is sometimes very limiting. I spent a little while reading through why’s poignant guide to ruby and then read through a couple of other online tutorials ( links below in other question), but for the most part I used the rails scaffold code to figure some of the basics out. While it doesn’t teach a lot of ruby, it is really helpful for learning the basics of rails and gives you enough ruby to do the basic stuff needed for simple webapps.

Like I said below, just jumping right into the rails tutorial on the homepage and then watching some screencasts is a pretty great way to go about starting.

Your link to YouRenew in the sidebar is not working. If you try http:// before what you have now, does it help?

woops!

Thanks for that. I didn’t realize it was broken.

Any books or other resources you could point me to for learning code? I have zero technical background, but want to learn/struggle. Thanks for the advice in advance.

See below!

Thanks!

James

Hi James, really liked your post on starting out web programming. I have some programming experience, but mostly in C due to my electronics background. I am a complete novice right now when it comes to the web and no idea where to start.

Can I start learning Ruby/Rails without knowing HTML/CSS/HTTP ? What resources did you use to learn Rails ? Also, you talked about monetizing your ruby "app". I have no idea how one makes money by writing a "web application". Can you give me more info on how to go about it ?

Sorry if my questions sound too generic but I have to start somewhere. Thanks for the nice post.

Anonymous

I knew no html/css when I started and have picked up pieces as I moved along. The learning curve is slow at first (like everything) but much easier to pick up once you get started. I think the best way to start is install ruby on rails using the rails site. Rails has this awesome function for learners called “Scaffolding”. Basically you write a command in your console and it will automatically generate some clean, easy to read to code. You can change bits and pieces to get it to function differently. That is how I started and it worked pretty well. Here are some awesome resources that I used also.

http://www.scribd.com/doc/8545174/Whys-Poignant-Guide-to-Ruby
http://articles.sitepoint.com/article/learn-ruby-on-rails
http://ror.kateray.net/post/1312957018/the-internet-teaches-you-ruby-on-rails
http://www.railscast.org/
http://guides.rubyonrails.org/index.html

The last one being probably the best one to read through first. Also, using stackoverflow and google for search is something that I do everytime i get in front of my computer - typing in console and browser error messages usually comes up with a few responses of someone with a similar issue. A lot of people have already dealt with the problems you will face early on. Another small point is that programming on windows is really tough. I switched over to linux and that made life a lot easier.

Let me know if you need help with any specifics!

James

Vote_fu with Rails 3 and JQuery

I had a pretty tough time with this, it is my third “major” project after working on the Amazon API and authlogic. It took around 2 weeks and to figure this out, so I hope that my notes will help you suffer less.

First thing was to install the gem Here. I am using the thumbs up gem, as opposed to vote_fu, either works though. The installation instructions are really straight forward in terms of putting your acts_as_voter and acts_as_voteable in your Voteable and User models. Follow the instructions in the Readme.

Next step is try some of the stuff in the examples in your console. For example if you have a Car Model and a User Model, you could try this: User.all[0].vote_exclusively_for(Car.all[0]). This would be the first user voting for the first car.

If this spits out a hash with “True” in it, then you know this is working. Next up is adding the below code to your Voteable Model (again, the model you are voting on)

I am using GET from the browser a parameter of “up” that I will then use to process the code in the following line. Next, I need to set up the routes, so that they will work with this method in my controller. This is probably where I struggled the most in setting up this whole process. I got some fantastic help from stackoverflow, which is what i finally used to figure it out.

With the vote method set up in my routes, I know was able to use this books_vote_path in my link_to request. The link_to example below will hopefully make more sense. When I “get” the url books/1/vote?vote=up - it creates a record in the databse on the Vote Model (which is created by the thumbs up gem).

In my view is the example of link_to. I started off working with this in the /books/1 “show” view for simplicity reasons.

The app should be working at this point without JQuery if you have followed all these steps. Voting up or down should create a new record in the DB. Adding the Jquery was pretty straight forward once I upgrade my rails.js file for Rails 3, and got the routes working correctly with the HTML.

I did not do this, but you can install jquery really easily using this link. Basically it is getting rid of the js files you don’t need, and adding the ones you do need.

In the code below I did not use around the div tags because it messed up the html on this page ( I know - lazy not to figure out how to fix that). You can use my code below, but I am not going to try to explain it because I think Ryan Bates does a wayyyy better job with railscast. I will post some of the resources I used below. Please let me know if you have any questions!



// votes_controller.rb
 def vote
    @book = Book.find(params[:id])
      if params[:vote] == 'up' #When you send the parameter of "Up" from the, it will recognize it and execute the next line of code
        current_user.vote_exclusively_for(@book) #I am using the thumbs up voting type here, I would advise doing the same as this code isn't tested with vote_fu
        flash[:notice] = "Thanks for Voting"
      elsif params[:vote] == 'down'
        current_user.vote_exclusively_against(@book)
        flash[:notice] = "Thanks for Voting"
     end

        respond_to do |format|
        if current_user.votes
        format.js {render :content_type => 'text/javascript'}
        format.html { redirect_to(@book, :notice => 'Thanks for voting!') }
        else
        format.js {render :action => "error" }
        format.html { render :action => "show" }
        end
    end
  end

//routes.rb

 resources :books do
        get :vote, :on => :member
        end

// /views/books/index.show.html

<div id="votes">
<%= link_to "Vote Up", vote_book_path(@book, :vote => "up"), :class => 'book_votes', :remote => :true, :method => :post %>
<%= link_to "Vote Down", vote_book_path(@book, :vote => "down"), :class => 'book_votes', :remote => :true, :method => :post %>
</div>
</a>
//application.js
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})

$(document).ready(function (){
$('.book_votes').click( function (){
$.get(this.href, $(this).serialize(), null, "script");
return false;
})
})


// views/books/vote.js.erb
$('#votes').before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>').fadeIn('slow');
$('#votescount_<%= @book.id %>').html('<%= @book.votes_for - @book.votes_against %>').fadeIn('slow');=> :true, :method => :post %>