Justkez

Trying to be a consistent blog 
Filed under

ruby

 

Generating a Tag Cloud in Jekyll

The term category and tag are used interchangeably throughout this posting; they are assumed to be the same thing.

Having recently adopted Jekyll to power this website, I have been doing a bit of hacking/extending to get some added features in. A few days ago it was integrating Twitter with Jekyll, and now it's generating a tag cloud.

Back in July, Alex Young blogged about his Jekyll migration, and thoughtfully included a link to some code he wrote to list all posts broken out by category/tag.

I wanted to take this a bit further, and generate a per-category page which listed all the postings for that category, but also to generate a tag cloud.

Generating tag pages

After making a few changes to Alex's code, I ended up with a tag stub in a Rakefile which loops through all the categories used on the site and generates a static HTML page with a list of all the postings in that category.

Remember that this code snippet requires you to define your per-post categories in the YAML header of each post, e.g.

    categories:
  - jekyll
  - blog
  - ruby
  

(You need to mkdir tags in your Jekyll directory before executing the code below)

Now, the Rakefile segment:

    desc 'Generate tags page'
task :tags do
  puts "Generating tags..."
  require 'rubygems'
  require 'jekyll'
  include Jekyll::Filters
  
  options = Jekyll.configuration({})
  site = Jekyll::Site.new(options)
  site.read_posts('')
  site.categories.sort.each do |category, posts|
    html = ''
    html << <<-HTML
---
layout: default
title: Postings tagged "#{category}"
---
    <h1 id="#{category}">Postings tagged "#{category}"</h1>

    html << '<ul class="posts">'
    posts.each do |post|
      post_data = post.to_liquid
      html << <<-HTML
        <li>#{post_data['title']}</li>
      HTML
    end
    html << '</ul>'
    
    File.open("tags/#{category}.html", 'w+') do |file|
      file.puts html
    end
  end
  puts 'Done.'
end
  

There is also a gist here

Now you can run rake tags and it will generate a number of HTML files in the tags/ subdirectory; regenerating through Jekyll will then copy these files over to your site. Navigating to /tags/jekyll.html should list all your Jekyll related posts.

Generating your tag cloud

The below snippet does something similar, but just loops through each category and counts the number of tagged postings. It then does some very rudimentary font-size scaling to make the more popular tags bigger.

    puts 'Generating tag cloud...'
require 'rubygems'
require 'jekyll'
include Jekyll::Filters

options = Jekyll.configuration({})
site = Jekyll::Site.new(options)
site.read_posts('')

html =<<-HTML
---
layout: default
title: Tag cloud
---

<h1>Tag cloud</h1>

    HTML

    site.categories.sort.each do |category, posts|
      html << <<-HTML
      HTML
      
      s = posts.count
      font_size = 12 + (s*1.5);
      html << "twitter?) with any queries or improvements, or post a comment below.

Filed under  //   blog   geekery   jekyll   ruby  

Comments [2]

Integrating Twitter with Jekyll

Having migrated Justkez.com to be based on Jekyll, I was pondering how I might include my recent twitterings on the front page of the site. In the Wordpress world, this would have been done via a plugin which may or may not have hung the loading of the page, might have employed caching, but would certainly have had some overheads.

Not in Jekyll.

Integrating Jekyll and Twitter

It is rather simple to create a Ruby script to pull down your most recent Twitter updates and dump them into a file.

It is also simple to wrap each update in some rudimentary HTML.

We then use the Liquid include tag to insert the updates where desired.

The Ruby script

I have the following sat in my ~/bin/ directory, which can be executed by a cron job at whatever interval you see fit:

[code] require 'twitter' twitter_user = 'JohnDoe' # TODO: Change to your Twitter username puts '<ul id="twitter_list">' Twitter::Search.new.from(twitter_user).each do |r| d = DateTime.parse(r.created_at).strftime('%d %b') puts "<li><span class=\"gentle\">#{d}</span> #{r.text}

All this does is fetch the latest updates for twitter_users, and wrap each one in an HTML <li> tag.

Including the HTML

This one is pretty straightforward, as Liquid (Jekyll's templating engine) supports the inclusion of partials/fragments.

  • Create an _includes directory in your Jekyll directory (not in _site)

  • Add the liquid include line {% include twitter.html %} where the latest updates will go (for me this was in index.html)

  • Now you can populate _includes/twitter.html by running the above Ruby script and dumping the output to file, ala: ruby ~/bin/script.rb > _includes/twitter.html.

Finishing up

You will need to regenerate the site, and you would ideally run it in auto mode. Now, whenever the cron job updates the HTML file, Jekyll will regenerate the relevant files for you.

There you have it, seamless Twitter and Jekyll integration.

Filed under  //   blog   geekery   jekyll   ruby   twitter  

Comments [0]

Getting to grips with Jekyll

Justkez.com » A brief posting

Having recently heard about Jekyll from a Hacker News posting, I felt the urge of website change stir within me.

Justkez.com has been run through Wordpress for many years now (since 2006), but it has got to a point where I just wasn't comfortable with it - felt too clunky.

Why Jekyll?

Jekyll is a static site generator written in Ruby, with support for Markdown documents for blog posts. I have been writing postings in Markdown for the last year, so it seemed a logical move. With the working world taking over a lot of my time, I become less bothered about the latest plugins, or publishing widgets.

As such, a few of the old posts didn't make the cut, and some of the more popular ones have been polished up a bit; there shouldn't be too many lost links.

Speed boost

In a very rudimentary test, the previous Wordpress version could dish out 3.5 requests per second (ab -n 20), the statically generated Jekyll version is serving at 24 requests per second; something you would call statistically significant.

Living with Jekyll

As a result of the migrating...

  • Writing posts is much more straightforward, and I can punt the markdown files around on whichever computer is to hand

  • I have learned to --love-- use the Liquid templating engine; not quite my ideal, but it works

  • I am a lot more inclined to put postings together; everything just feels cleaner

So, if you're tempted by any of the above, give Jekyll a go.


Justkez.com is produced by Kester Dobson.
Feed icon There is an Atom feed available for syndication, and some archives to browse.

Filed under  //   blog   geekery   jekyll   ruby   server  

Comments [0]

User authentication with Ramaze

This post outlines how to get database driven, session based user registration, authentication and login working with Ramaze and Sequel.

Introduction

Ramaze is, in my opinion, the leading Ruby web framework. Whilst the documentation and community examples are still gaining momentum, you can really benefit by spending some time learning the framework.

This post is a guide to getting user registration and authentication working. We are talking proper, database driven, session based authentication - not just a single secure login area.

Requirements

This guide is written based on Ramaze 2009.06 and Sequel 3.2.0 - earlier versions of both of these will work, but you will need the "Innated" version of Ramaze (something after 2009.05 would be best) and ideally Sequel 3.1.0. (Sequel is the database abstraction/ORM library, so you can be using SQLite, MySQL, Pg - whatever floats your database boat)

Getting started

We will be creating and editing several files - one Sequel-based database model, two controllers and a couple of view files. To save you some effort, I have provided a basic SQL schema for creating the user table.

    create table users(
  id integer primary key,
  login varchar default null,
  email varchar default null,
  crypted_password varchar default null,
  salt text default null
  );
  

N.B. this is a SQLite formatted query.

We also need to make sure the Ramaze application is using Sequel, and we connect Sequel to the database with the above table. In this example, I have created tutorial.db3 with the above table.

In app.rb, add in the following lines:

    require 'sequel'
# ...
DB = Sequel.connect('sqlite://tutorial.db3')
  

The controller

The controller, as you probably know, ties the interface (view) and database (model) together, we don't actually need to do much with regards to controller editing, as Ramaze has pretty sophisticated helpers for managing user logins.

With a new Ramaze project (ramaze --create my-project), open the controller/init.rb file and make it look like this:

    class Controller < Ramaze::Controller
  layout :default
  helper :xhtml, :user
  engine :Etanni
end

private

 def login_first
   return if logged_in?
   redirect MainController.r(:login)
 end

require __DIR__('main')
  

Important things to note are the inclusion of the :user helper; you can read up on this built-in helper here.

As a final step, we need to add two methods to controller/main.rb - this will handle the login and registration process:

    def login
  @title = "Login"
  redirect_referer if logged_in?
  return unless request.post?
  user_login(request.subset(:login, :password))
  redirect MainController.r(:index)
end

def register
  @title = "Register for an account"
  
  if request.post?
    @user = ::User.new
    @user[:email] = request[:email]
    @user.password = request[:password]
    @user.password_confirmation = request[:password_confirmation]
    @user.salt = Digest::SHA1.hexdigest("--#{Time.now.to_f}--#{user.email}--") 
    
    if @user.save
        flash[:message] = 'Account created, feel free to login below'
        redirect MainController.r(:index)
    end
  end
end
  

And whilst we're at it, we might as well add in the very, very basic logout action:

    def logout
  flash[:message] = "Logged out"
  user_logout
end
  

The model

We need to define several model-level methods to help register, authenticate and login users. This will model will be a representation of the users table that you should have created in the first step.

Create a file, "user.rb", in model/, and you can make it look something like this:

    class User < Sequel::Model(:users)  

  attr_accessor :password
  attr_accessor :password_confirmation

  def after_create
    self.crypted_password = encrypt(password)
    @new = false
    save
  end

  def authenticated?(password)
      crypted_password == encrypt(password)
  end

  def encrypt(password)
    self.class.encrypt(password, salt)
  end

  def self.encrypt(password, salt)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  end

  def self.authenticate(hash)
    email, pass = hash['login'], hash['password']

    if user = User[:email => email]
      return user unless pass
      user if user.authenticated?(pass)
    end
  end

end
  

Some interesting things to note...

  • We have user defined attributes for the password and password_confirmation fields - we do not store these in the database, but we need to assign and access them to verify the password and then hash it

  • The password is hashed by using SHA1, with a seed/key determined from the user password and another user-specific value stored as the salt. This means that each password is hashed differently, making things a bit more secure.

  • There is a bit of overlap between login and email here, as there will be in the following sections. I have chosen to demonstrate logging in with an email address, not a login name.

Don't forget...

You need to edit model/init.rb to require the model you just created.

The views

We will need to create two views, in addition to the default views that Ramaze creates for you. (Remember, a view is the webpage/interface that you actually see in the browser)

The login form

Create login.xhtml in view/, and use the following as a guide:

    <form action="#{MainController.r(:login)}" method="POST">
  <fieldset>
    <legend>Login form</legend>

    <label for="login">E-mail address</label>
    <input type="text" name="login" />

    <br /><br />

    <label for="password">Password</label>
    <input type="password" name="password" />

    <br /><br />

    <input type="submit" value="Login" />

  </fieldset>
</form>
  

Believe it or not, we have nearly finished adding in user authentication; the final steps being the ability to register, and securing parts of your application.

The registration form

It is more than likely that you will want a slightly more exotic registration than the one below, but it provides a good starting point. This code should go in view/register.xhtml:

    <form action="#{MainController.r(:register)}" method="POST">
  <fieldset>
    <legend>Register</legend>

    <label for="email">E-mail address</label>
    <input type="text" name="email" />

    <br /><br />

    <label for="password">Password</label>
    <input type="password" name="password" />

    <br /><br />

    <label for="password_confirmation">Password confirmation</label>
    <input type="password" name="password_confirmation" />

    <br /><br />

    <input type="submit" value="Register" />

  </fieldset>
</form>
  

Securing actions, accessing the logged in user

There are a couple of ways you can secure actions from within Ramaze - one of which you can see in the login method above. These techniques are:

    def secret
  login_first
  # ... Secret stuff
end
  

Which will redirect the user to the login page. Alternatively, you can do a "manual" check to see if the user is logged in:

    def secret
  redirect_referer unless logged_in?
  # .. or ..
  return if !logged_in?
  # .. or ..
  if logged_in?
    print 'You are logged in'
  end
end
  

Who is logged in?

It can be very useful to access information about the logged in user; if we use an example whereby a user wants to edit their comment on a blog...

    def edit_comment id = nil
  login_first
  redirect_referer unless @comment = Comment[:id => id]
  redirect_referer unless @comment[:user_id] == user[:id]
end
  

This hypothetical snippet would ask the user to login first, then it would check to see if the comment exists. Finally it will check the user object to see if the ID lines up with the owner of the comment.

It is worth noting that user is accessible throughout almost all of the Ramaze application, so you can do checks within layouts, views, controllers (but not models).

Questions/problems?

Feel free to leave me a comment below with any issues you're having with the above, or if you have some improvements that can be made.

Filed under  //   development   programming   ramaze   ruby   tutorial  

Comments [0]

Validating data with Sequel

Sequel is a pretty nifty database toolkit for Ruby. I have been using it lately in combination with Ramaze - an incredibly fast and powerful web framework (also for Ruby).

Sequel has perfect documentation in terms of an API, and the cheatsheet and readme give a good summary of how to use the features, but there's not too much out there that can gives a gentle introduction.

There will be a few posts published here that try and give such an introduction to various aspects of Sequel.

This one is a quick run down of how you validate database fields in Sequel.

The model to validate

For this example, we will use a simple User model - not very original, I know:

    class User < Sequel::Model(:users)
  set_schema do
    primary_key     :id
    varchar         :username
    varchar         :email
    int             :house_number
    varchar         :zip_code
  end
end
  

Basic validation

Sequel comes with some built in validation rules which will satisfy most people's needs straight off. They are mentioned at the bottom of the readme, and detailed in the RDoc page (scroll down a bit to see everything).

A brief demonstration of some of these validations, in context:

    validations.clear
validates do
  presence_of       :username, :email, :house_number
  uniqueness_of     :email
  numbericality_of  :house_number
  length_of         :username, :minimum => 6, :maximum => 10
  format_of         :zip_code, :with => '/(^\d{5}$)|(^\d{5}-\d{4}$)/'
end
  

Pretty self explanatory stuff when you see it in action.

The presence_of makes sure all fields specified have been given a value, uniqueness_of makes sure that there is only one instance of that value in the table (useful when registering users by e-mail address, for example).

The format_of allows you to validate a specified field against a regular expression. I'm not actually sure if that ZIP code regex works, don't quote me!

These will be run when you try and save a new model or update an existing one - if the model fails to save, the instance of the model will be returned with the errors property set - you can inspect this to see what the problem is.

Advanced validation

As mentioned, the basic validation should be sufficient for most people, but not everyone. I have found myself needing to validate fields with a higher degree of control. For example, you might want to use some obscure criteria, or need to perform some action on the field to see if it is valid or not.

To demonstrate this, we will only allow odd house numbers to be saved:

    validates_each :house_number do |object, attribute, value|
    object.errors[attribute] << 'No even house numbers allowed!' unless value % 2 == 1
  end
  

You would put this underneath the previous code block.

You could call a method that returns true or false based on whatever critera you choose:

    #...
unless checkMyValue value
  

Filed under  //   development   programming   ruby   sequel   tutorial  

Comments [0]