Publicerad 1 september, 2009 av Mikael Forsberg
Hpricot är en HTML parser som är skriven i Ruby. Jag gillar den eftersom den är snabb och extremt enkel att jobba med. Den är perfekt om man vill extrahera innehåll från en webbsida som inte tillhandahåller ett färdigt api. Det finns många bra tutorials på nätet.
Ett enkelt exempel
För att till exempel hitta alla nyheter på Athegas första sida kan man göra så här.
require 'rubygems'
require 'open-uri'
require 'hpricot'
# Läs in Athegas första sida
doc = Hpricot(open("http://athega.se"))
# Xpath uttryck för att hitta nyheterna
result = doc/"//*[@id='helplist']/li/a"
Hpricot på Google App Engine
Jag ville använda Hpricot tillsammans med Jruby och Sinatra (som Peter har skrivit mer om) på Google App Engine. Jag följde den här guiden för att komma igång med min Sinatra applikation på App Engine och det gick smärtfritt. Tyvärr så small det direkt när jag försökte använda mig av Hpricot. Ett AccessControlException kastades.
javax.servlet.ServletContext log: Application Error
java.security.AccessControlException: access denied (java.net.SocketPermission athega.se resolve)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkPermission(DevAppServerFactory.java:128)
at java.lang.SecurityManager.checkConnect(SecurityManager.java:1031)
at java.net.InetAddress.getAllByName0(InetAddress.java:1145)
at java.net.InetAddress.getAllByName(InetAddress.java:1083)
at java.net.InetAddress.getAllByName(InetAddress.java:1019)
at java.net.InetAddress.getByName(InetAddress.java:969)
Vilket tyder på att någon javaklass som inte är med på Googles lista över tillåtna klasser användes. När jag studerade stacktracet lite närmare märkte jag att det var open-uri som ville använda javaklassen InetAddress som inte finns med i listan på godkända klasser.
Eftersom man med hjälp av Jruby kan ”scripta” java var det relativt enkelt att byta ut open-uri mot godkända javaklasser istället och sedan automagiskt göra om java InputStream objektet till ett ruby io objekt som Hpricot kan ta i sin konstruktor. Lösningen blev enligt nedan.
require 'rubygems'
require 'hpricot'
# Importerar java istället för open-uri
require 'java'
# Skapa en instans av java-klassen URL
url = java.net.URL.new("http://athega.se")
# Kasta om java inputstreamen till ett ruby io objekt
ruby_io = org.jruby.RubyIO.new(JRuby.runtime, url.openStream)
io = Java.java_to_ruby(ruby_io.java_object)
# Sen är det bara att använda Hpricot som vanligt
doc = Hpricot(io)
result = doc/"//*[@id='helplist']/li/a"
Sedan transformerade jag resultatet till JSON och la upp applikationen här http://athega-news-api.appspot.com (OBS, applikationen returnerar JSON direkt så jag rekommenderar JSONView pluginet till Firefox om man vill titta på datan)
Om någon vill titta närmare på koden ligger den på Github men tänk på se till så att ni har tillstånd av rättighetsinnehavaren innan ni plockar data från webben.
Etiketter: Google App Engine, Hpricot, Java, JRuby, Sinatra
Postat i Kod | Inga kommentarer »
Publicerad 5 maj, 2009 av Peter Hellberg

Presentationen: jruby-railsconf-2009.pdf
Huvudpunkterna på föreläsningen var:
- Installation
- Utveckling
- Prestandatestning
- Interaktion med Java
- Testning
Jag hade gärna sett att han gått in lite mer på djupet om skillnaderna
mellan JRuby och MRI, Olika sätt att deploya (Han visade GlassFish,
med tveksamt resultat)
Ett verktyg jag helt klart kommer att använda mig av i framtiden är VisualVM.
A Hat Full of Tricks with Sinatra – Blake Mizerany (Heroku)
Det första som händer är att Christian Neukirchen,
huvudutvecklaren av Rack sätter sig brevid mig.
Första fjärdedelen av Blakes föreläsning handlar inte om
Sinatra över huvud taget utan om just Rack.
Minimal Rack-applikation
run lambda { |env| [200, {'Content-Type' => 'text/html'}, ['Hello']] }
Minimal Sinatra-applikation
require 'sinatra'
get '/' do
'Hello'
end
Rekomenderade böcker
Exempelkod
Relaterade länkar

Etiketter: JRuby, RailsConf, Ruby, Sinatra, Tutorials
Postat i Konferens, Webbutveckling | Inga kommentarer »
Publicerad 8 mars, 2009 av Peter Hellberg
Efter att ha sett scriptet follow_iphone_devs.rb fick jag idén att kombinera Sinatra och google-spreadsheet-ruby som ett exempel på vad man kan göra i Ruby om man inte har behov av ett större ramverk som Ruby on Rails.

Sinatra är ett DSL för att snabbt och enkelt skapa webbapplikationer i Ruby:
# myapp.rb
require 'rubygems'
require 'sinatra'
get '/' do
'Hello world!'
end
Komma igång
Först av allt behöver vi Ruby och RubyGems. Eftersom jag använder OS X så är de redan installerade. (Men se till att du har en uppdaterad version av RubyGems)
Om du inte redan har lagt till GitHub i listan över dina gem sources:
gem sources -a http://gems.github.com
Nu är vi redo att installera de gems vi kommer att använda:
sudo gem install sinatra gimite-google-spreadsheet-ruby haml
Nu är vi redo att börja skriva vårt script:
sinatra-and-google-spreadsheet.rb
Först tar vi och laddar in de gems vi kommer använda (Sinatra kommer att ladda in Haml åt oss):
require 'rubygems'
require 'sinatra'
require 'google_spreadsheet'
Nu tar vi och aktiverar sessioner:
# We want to save the posts in a session variable
set :sessions, true
OBS: Detta är självklart ingen lösning som skalar men det fungerar bra för vårt syfte.
Metoderna för att hämta datat från Google Spreadsheet:
def get_posts
# Retrieve and return the posts
session["posts"] ||= retrieve_posts
end
def retrieve_posts
# Empty posts hash
posts = {}
# Log in to Google Spreadsheet
session = GoogleSpreadsheet.login('xxxxxxxx', 'xxxxxxxx')
# First worksheet of
# http://spreadsheets.google.com/ccc?key=peGTxx6h1WT0ihw9-QETbQg
ws = session.spreadsheet_by_key("peGTxx6h1WT0ihw9-QETbQg").worksheets[0]
# Iterate over all the rows in the spreadsheet
for i in 1...ws.num_rows
# Store the data Identify data using the slug
posts[ws[i+1,5]] = {
:title => ws[i+1,1],
:content => ws[i+1,2],
:date => Date.parse(ws[i+1,3]),
:author => ws[i+1,4],
}
end
# Return the retrieved posts
posts
end
Vi använder memoization för att det bara ska bli en förfrågning till Google per session
Startsida listar helt enkelt alla poster:
get '/' do
# Get all the posts
@posts = get_posts
# Render the index template
haml :index
end
Respektive post har en ”slug” som används i urlen:
get '/:slug' do
# Get the post
@post = get_posts[params[:slug]]
# Render the post template
haml :post
end
Nu definierar vi de Haml-mallar vi använder på sidorna:
__END__
@@ layout
%html
%head
%title Sinatra and Google Spreadsheet
%body
= yield
@@ index
%h1.title All posts
- @posts.each do |slug, post|
%ul
%li
%a{:href => slug}
= post[:title]
@@ post
%h1.title
= @post[:title]
%div
Published
%span.date
= @post[:date]
by
%span.author
= @post[:author]
%p.content
= @post[:content]
%a{:href => '/'} << Back
Starta scriptet
$ ruby sinatra-and-google-spreadsheet.rb
== Sinatra/0.9.1 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.0.0 codename That´s What She Said)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop
Om allt går som det ska så ska vi nu ha en server på http://localhost:4567/.
Resultat
Från ett kalkylblad på Google Spreadsheet:

Data från Google Spreadsheet
Till en sida serverad med Sinatra:

Resultatet
Att man kan åstadkomma så mycket med under 100 rader Rubykod är rätt imponerande, speciellt om man tänker på att vi då även räknar in kommentarer och mallar.
Etiketter: Google, Haml, Ruby, Sinatra
Postat i Kod, Webbutveckling | 1 kommentar »