Posts tagged as:

mysql

In this episode we discussed:

{ 5 comments }

Here’s a list of several of the things we discussed:

To hire Josh’s guys, go to http://pgexperts.com.

Download 6.08 MB
Download (iPod & iPhone) 4.61 MB

{ 5 comments }

Here is what I’ve done to create this application:

  1. Use the ‘rails new’ command to create a rails application
  2. Set up the Gemfile
  3. Configure the Database
  4. Install Cucumber
  5. Install Rspec
  6. Install Devise
  7. Install CanCan
  8. Install jQuery
  9. Configure Devise

Download (HD) 84.2 MB
Download (iPod & iPhone) 47.4 MB

{ 37 comments }

A few weeks ago, I wrote 9 Ways to Use Rails Metal. The third way to use Rails Metal was implementing a simple API.

Before I provide the code and an explanation, I’d like to cover a few things. First, this API only requires an API key. If you want an authentication token or some other identifier, look at Rails Metal Example #1: Authentication for some ideas of how to manage authentication for your API. Second, I didn’t filter any contents on the User model, so the password is passed back by the API. A simple delete call for the keys you want to omit from the hash returned by the find_by_sql call should clear up any data you don’t want to pass back.

Here’s the code:

# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)

class ApiHandler
  def self.call(env)
    req = Rack::Request.new(env)
    @params = req.params
    if (env["PATH_INFO"] =~ (/^/api/(xml|json)/user//)) && ApiKey.find_by_key(@params["key"]) && env["REQUEST_METHOD"] == "POST"
      format = env["PATH_INFO"].split("/")[2]
      return [200, {"Content-Type" => "application/xml"}, [User.find_by_sql(["SELECT * FROM users WHERE id = ?", @params["id"]]).to_xml]] if format == "xml"
      [200, {"Content-Type" => "application/json"}, [User.find_by_sql(["SELECT * FROM users WHERE id = ?", @params["id"]]).to_json]] if format == "json"
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end

The code here is pretty simple. You check the path to make sure it matches the api path for XML or JSON, then find the record, convert it, and send it off to the user.

A few things to note are that the Rails methods to_json and to_xml are available in Rails Metal. I also had to explicitly return on the xml format because it isn’t the last execution and therefore isn’t returned.

{ 1 comment }

Rails Developers do it with Models

I saw a t-shirt at Mountain West RubyConf this year that said “Rails Developers do it with Models.” Of course, they were talking about the classes we use to access the database. In fact, in Ruby on Rails, when you think of your data, you usually think of Models, not the database. The database is more a mechanism for remembering the data when you’re not using it.

Let’s look at one of the files that were generated in Part I. This file is where the model for our blog’s posts are defined.
[click to continue…]

{ 0 comments }

Last week, I wrote a post listing 9 ways to use rails metal. This is an explanation of the first way to use Rails Metal: Check Authentication.

We’re setting up this Rails Metal to handle two scenarios: requiring authentication, and logging the user in. First, it verifies that requests to any path beginning with /admin have a user logged in by checking that a valid user id is stored in the session. Second, accepts an HTTP POST to /authenticate with a username, password, and target path for redirect upon

Before we get too deep into things, let me share the code. You can check out a working version of the application on GitHub.

Here’s the Metal.

require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)

class Authentication
  def self.call(env)
    session = env["rack.session"]
    request = Rack::Request.new(env)
    params = req.params
    if env["PATH_INFO"] =~ /^/admin.*/ && (session["user_id"].nil? || (session["user_id"] && User.connection.select_all("SELECT * FROM users WHERE id = #{session["user_id"]}").empty?))
      [302, {"Content-Type" => "text/html", "Location" => "/login?target_path=#{env["REQUEST_URI"]}"}, ["You must be logged in to view this page. You are being redirected."]]
    elsif env["REQUEST_METHOD"] == "POST" && env["PATH_INFO"] =~ /^/authenticate/
      users = User.connection.select_all("SELECT * FROM users WHERE username='#{params["user"]["username"]}' AND password='#{params["user"]["password"]}'")
      unless users.empty?
        session["user_id"] = users.first["id"]
        [302, {"Content-Type" => "text/html", "Location" => "#{params["target_path"] || "/"}"}, ["You have been logged in. You are being redirected."]]
      else
        [302, {"Content-Type" => "text/html", "Location" => "/login"}, ["Authentication failed. You are being redirected."]]
      end
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end

Bases for Understanding the Code

There are a few methods and structures here that are important to note in how this all works.

First, the self.call(env) is the entry point for the Rails Metal application. env refers to the Rack environment.

Next, Rack::Request is a class that provides a convenient interface to the Rack environment. In this case, we’re using it to extract the GET and POST parameters from the request. You can see the full Rack::Request API here.

env[‘rack.session’] returns the session as a hash. In this case, keys are accessed as strings, not symbols. So, in our case, session[:user_id] won’t work, but session[“user_id”] will work.

Finally, we do direct SQL calls through the ActiveRecord object rather than using the find method because it’s faster and we only need a few fields like the id of the user that’s found.

Overview of the Code

Checking Authentication

Our authentication code creates a session variable called user_id and places the current user’s id into it. So, to make sure someone is logged in, we need to make sure that a valid user id is in the session.

To do this we check if the path begins with /admin. env[“PATH_INFO”] contains the path without any GET parameters. So, we do a regular expression match and them move on to checking if there’s a valid user id in the session.

There are two scenarios that we need to check. There is no user id stored in the session and the user id stored is not the id of a valid user.

If both conditions are met, we redirect the user to /login and pass along the path the user was trying to access as a GET parameter called “target_path.”

Logging the user in

If a valid username and password are passed to the path /authenticate, we need to store the user’s id in the session and redirect them to where they were trying to go. To check the username and password, we query the database. If no matches are found in the database, then we send them to the login page.

Overall the code is pretty simple. Feel free to use it in any of your applications. I’ll post the benchmarks when I get a chance, but several people were looking for this content, so I’m putting it out now.

{ 7 comments }

Most people are fairly familiar with the basic idea of a database driven website. More specifically, they understand that data is stored in the database and somehow makes it into the web page the browser loads.

Ruby on Rails provides a framework that makes the transition of information from a database to a webpage extremely simple. To begin, we’ll assume that you have Ruby and Rails installed on your machine. From there, we’ll work our way up from the database to the core of the application and back out into the webpage that presents the data.
[click to continue…]

{ 1 comment }

I recently set up a relevance search plugin for Ruby on Rails. If you want it, you can follow this link to check out the plugin. If you want to know about the plugin’s algorithm and limitations, read on.
[click to continue…]

{ 0 comments }

I’ve been working on NORM and after a few proof of concept things, I wrote a test to test the create method for the base class.

Here’s the create method:

def create(attributes)
  columns = attributes.keys.join(", ")
  values = attributes.collect {|k, v| "'#{v}'"}.join(", ")
  @@connection.execute("INSERT INTO base (#{columns}) VALUES (#{values});")
  new(attributes)
end

I’ve written dozens of unit tests in Ruby on Rails, but what I didn’t realize was that the same library you use to test in Rails is the standard test/unit library came with the Ruby installation on my Windows machine. So, I wrote my own test which I later revised. [click to continue…]

{ 0 comments }

I’m pretty sure that the Ruby DBI gem is deprecated, however, I’m writing NORM so that I can experience the pain of refactoring as well as proving out that Test Driven Design has a place in modern development. It also helps because previous to the project I’m currently working on for my job at SolutionStream I have not used TDD and I’m anxious to see its power in a project like this one.

I found I needed the MySQL adapter for my application—I’m only building for MySQL right now. That required the dbd-mysql gem in addition to the dbi gem. After installing the gems, I found I could require them and then use them to connect to the database. Here are some code snippets from NORM that show how I’m using the DBI library.

First require the dbi and mysql gems.

require 'dbd/mysql'
require 'dbi'

Then, in my object, I’m storing the connection in a class variable.

@@connection = DBI.connect("DBI:Mysql:norm:localhost", "root", "")

Finally, I’m using the connection to perform operations on the database.

def create(attributes)
  columns = attributes.keys.join(", ")
  values = attributes.collect {|k, v| "'#{v}'"}.join(", ")
  @@connection.execute("INSERT INTO base (#{columns}) VALUES (#{values});")
  new(attributes)
end

{ 0 comments }