Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit of plain application

  • Loading branch information...
commit 404d29862d3ae8855cd4e917ed8d91f191a206e4 0 parents
Karel Minarik authored
6 .gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+*/.DS_Store
+log/*.log
+tmp/**/*
+config/config.yml
+config/deploy.rb
2  Capfile
@@ -0,0 +1,2 @@
+load 'deploy' if respond_to?(:namespace) # cap2 differentiator
+load 'config/deploy'
67 Rakefile
@@ -0,0 +1,67 @@
+require 'rake'
+
+task :default => 'app:start'
+
+namespace :app do
+
+ desc "Install the fresh application"
+ task :install do
+ # TODO : Copy fixtures into <tt>./data/</tt>, open in Safari on a Mac, etc
+ Rake::Task['app:install:create_db'].invoke
+ end
+ namespace :install do
+ task :create_db do
+ require 'rubygems'
+ require 'activerecord'
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => './data/development.db')
+ load File.join( File.dirname(__FILE__), 'config', 'db_create_comments' )
+ end
+ task :create_sample_article do
+ end
+ task :create_sample_comment do
+ end
+ task :open_in_browser do
+ end
+ end
+
+ desc 'Start application in development'
+ task :start do
+ exec "ruby app/marley.rb"
+ end
+
+ desc "Deploy application on the server [shortcut for Cap]"
+ task :deploy do
+ exec "cap deploy"
+ end
+
+ desc "Run tests for the application"
+ task :test do
+ exec "cd app/test; ruby marley_test.rb"
+ end
+
+end
+
+namespace :blog do
+
+ task :sync do
+ # TODO : use Git
+ exec "cap blog:sync"
+ end
+
+end
+
+namespace :server do
+
+ task :start do
+ exec "thin -R rackup.ru -d -l log/production.log -e production -p 4500 start"
+ end
+
+ task :stop do
+ exec "thin stop"
+ end
+
+ task :restart do
+ exec "thin restart"
+ end
+
+end
228 app/marley.rb
@@ -0,0 +1,228 @@
+require 'rubygems'
+require 'ftools' # ... we wanna access the filesystem ...
+require 'yaml' # ... use YAML for configs and stuff ...
+require 'sinatra' # ... Classy web-development dressed in DSL, http://sinatrarb.heroku.com
+require 'activerecord' # ... or Datamapper? What? :)
+require 'rdiscount' # ... convert Markdown into HTML in blazing speed
+require File.join(File.dirname(__FILE__), '..', 'vendor/akismet') # ... filter spam from ham
+
+# ... or alternatively, run Sinatra on edge ...
+# $:.unshift File.dirname(__FILE__) + 'vendor/sinatra/lib'
+# require 'sinatra'
+
+CONFIG = YAML.load_file( File.join(File.dirname(__FILE__), '..', 'config', 'config.yml') ) unless defined? CONFIG
+
+# -----------------------------------------------------------------------------
+
+module Blog
+
+ DATA_DIRECTORY = File.join(File.dirname(__FILE__), '..', 'data') unless defined? DATA_DIRECTORY
+
+ # = Articles
+ # Data source is <tt>../data</tt> directory
+ class Post
+
+ attr_reader :id, :title, :perex, :body, :body_html, :meta, :published_on, :updated_on, :published, :comments
+
+ def initialize(options={})
+ options.each_pair { |key, value| instance_variable_set("@#{key}", value) if self.respond_to? key }
+ end
+
+ def self.all(options={})
+ self.find_all options.merge(:draft => true)
+ end
+
+ def self.published(options={})
+ self.find_all options.merge(:draft => false)
+ end
+
+ def self.[](id, options={})
+ self.find_one(id, options)
+ end
+
+ def categories
+ self.meta['categories'] if self.meta and self.meta['categories']
+ end
+
+ private
+
+ def self.find_all(options={})
+ options[:except] ||= ['body', 'body_html']
+ posts = []
+ self.extract_posts_from_directory(options).each do |file|
+ attributes = self.extract_post_info_from(file, options)
+ attributes.merge!( :comments => Blog::Comment.find_all_by_post_id(attributes[:id], :select => ['id']) )
+ posts << self.new( attributes )
+ end
+ return posts
+ end
+
+ def self.find_one(id, options={})
+ directory = self.load_directories_with_posts(options).select { |dir| dir =~ Regexp.new("#{id}") }
+ options.merge!( {:draft => true} )
+ # FIXME : Refactor this mess!
+ return if directory.empty?
+ directory = directory.first
+ return unless directory or !File.exist?(directory)
+ file = Dir["#{directory}/*.txt"].first
+ self.new( self.extract_post_info_from(file, options).merge( :comments => Blog::Comment.find_all_by_post_id(id) ) )
+ end
+
+ # Returns directories in data directory. Default is published only (no <tt>.draft</tt> in name)
+ def self.load_directories_with_posts(options={})
+ if options[:draft]
+ Dir[File.join(DATA_DIRECTORY, '*')].select { |dir| File.directory?(dir) }
+ else
+ Dir[File.join(DATA_DIRECTORY, '*')].select { |dir| File.directory?(dir) and not dir.include?('.draft') }
+ end
+ end
+
+ # Loads all directories in data directory and returns first <tt>.txt</tt> file in each one
+ def self.extract_posts_from_directory(options={})
+ self.load_directories_with_posts(options).collect { |dir| Dir["#{dir}/*.txt"].first }.compact
+ end
+
+ # Extracts post information from the directory name, file contents, modification time, etc
+ # Returns hash which can be passed to <tt>Blog::Post.new()</tt>
+ # Extracted attributes can be configured with <tt>:except</tt> and <tt>:only</tt> options
+ def self.extract_post_info_from(file, options={})
+ raise ArgumentError, "#{file} is not a readable file" unless File.exist?(file) and File.readable?(file)
+ options[:except] ||= []
+ options[:only] ||= Blog::Post.instance_methods # FIXME: Refaktorovat!!
+ dirname = File.dirname(file).split('/').last
+ file_content = File.read(file)
+ meta_content = file_content.slice!( self.regexp[:meta] )
+ body = file_content.sub( self.regexp[:title], '').sub( self.regexp[:perex], '').strip
+ post = Hash.new
+ # TODO: Cleanup regexp for ID
+ post[:id] = dirname.sub(self.regexp[:id], '\1').sub(/\.draft$/, '')
+ post[:title] = file_content.scan( self.regexp[:title] ).to_s.strip unless options[:except].include? 'title' or
+ not options[:only].include? 'title'
+ post[:perex] = file_content.scan( self.regexp[:perex] ).first.to_s.strip unless options[:except].include? 'perex' or
+ not options[:only].include? 'perex'
+ post[:body] = body unless options[:except].include? 'body' or
+ not options[:only].include? 'body'
+ post[:body_html] = RDiscount::new( body ).to_html unless options[:except].include? 'body_html' or
+ not options[:only].include? 'body_html'
+ post[:meta] = ( meta_content ) ? YAML::load( meta_content.scan( self.regexp[:meta]).to_s ) :
+ nil unless options[:except].include? 'meta' or not options[:only].include? 'meta'
+ post[:published_on] = File.mtime( File.dirname(file) ) unless options[:except].include? 'published_on' or
+ not options[:only].include? 'published_on'
+ post[:updated_on] = File.mtime( file ) unless options[:except].include? 'updated_on' or
+ not options[:only].include? 'updated_on'
+ post[:published] = !dirname.match(/\.draft$/) unless options[:except].include? 'published' or
+ not options[:only].include? 'published'
+ return post
+ end
+
+ def self.regexp
+ { :id => /^\d{0,4}-{0,1}(.*)$/,
+ :title => /^#\s*(.*)$/,
+ :perex => /^([^\#\n]+\n)$/,
+ :meta => /^\{\{\n(.*)\}\}\n$/mi # Multiline Regexp
+ }
+ end
+
+ end
+
+ # = Comments for articles
+ class Comment < ActiveRecord::Base
+
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => File.join(DATA_DIRECTORY, 'development.db') )
+
+ belongs_to :post
+
+ validates_presence_of :author, :email, :body, :post_id
+
+ before_create :check_spam
+
+ private
+
+ def check_spam
+ result = Blog::Akismet.check( self.ip, self.user_agent, self.referrer, nil, 'comment', self.author, self.email, self.url, self.body, nil )
+ puts result.inspect
+ if result
+ self.checked = true
+ self.spam = result
+ end
+ end
+
+ end
+
+ # = Interface to Akismet checking library
+ # TODO : Cleanup
+ class Akismet
+ def self.check( ip, user_agent, referrer, permalink, comment_type, author, email, url, body, other )
+ akismet = ::Akismet.new(::CONFIG['akismet']['key'], ::CONFIG['akismet']['url'])
+ raise ArgumentError, "Invalid Akismet key" unless akismet.verifyAPIKey
+ akismet.commentCheck(ip, user_agent, referrer, permalink, comment_type, author, email, url, body, other)
+ end
+ end
+
+end
+
+# -----------------------------------------------------------------------------
+
+helpers do
+
+ include Rack::Utils
+ alias_method :h, :escape_html
+
+ def human_date(datetime)
+ datetime.strftime('%d. %m. %Y').gsub(/ 0(\d{1})/, ' \1')
+ end
+
+end
+
+configure do
+ set_options :session => true
+end
+
+configure :production do
+ # 404.html
+ not_found do
+ File.read( File.join( File.dirname(__FILE__), 'public', '404.html') )
+ end
+ # 500.html
+ error do
+ File.read( File.join( File.dirname(__FILE__), 'public', '500.html') )
+ end
+end
+
+# -----------------------------------------------------------------------------
+
+get '/' do
+ @posts = Blog::Post.published
+ @page_title = "#{CONFIG['blog']['title']}"
+ erb :index
+end
+
+get '/:post_id.html' do
+ @post = Blog::Post[ params[:post_id] ]
+ throw :halt, [404, 'Post not found' ] unless @post
+ @page_title = "#{@post.title} #{CONFIG['blog']['name']}"
+ erb :post
+end
+
+post '/:post_id/comments' do
+ @post = Blog::Post[ params[:post_id] ]
+ throw :halt, [404, erb(not_found) ] unless @post
+ params.merge!( { :ip => request.env['REMOTE_ADDR'], :user_agent => request.env['HTTP_USER_AGENT'] } )
+ puts params.inspect
+ @comment = Blog::Comment.create( params )
+ if @comment.valid?
+ redirect "/"+params[:post_id].to_s+'.html#comments'
+ else
+ @page_title = "#{@post.title} #{CONFIG['blog']['name']}"
+ erb :post
+ end
+end
+get '/:post_id/comments' do
+ redirect "/"+params[:post_id].to_s+'.html#comments'
+end
+
+get '/about' do
+ "<p style=\"font-family:sans-serif\">I'm running on Sinatra version " + Sinatra::VERSION + '</p>'
+end
+
+# -----------------------------------------------------------------------------
22 app/public/404.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>404 Not Found</title>
+</head>
+
+<body>
+
+<div class="container">
+ <div>
+ <h1>404!</h1>
+ </div>
+ <h2>This page was not found.
+ Please <a href="/">return to homepage</a> and try to find your way.
+ </h2>
+</div>
+
+</body>
+</html>
22 app/public/500.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>500 Server Error</title>
+</head>
+
+<body>
+
+<div class="container">
+ <div>
+ <h1>500!</h1>
+ </div>
+ <h2>Something strange happened.
+ Please try to <a href="/">load the homepage</a> and come back later if you see this page again.
+ </h2>
+</div>
+
+</body>
+</html>
0  app/public/favicon.ico
No changes.
10 app/rackup.ru
@@ -0,0 +1,10 @@
+$:.unshift File.dirname(__FILE__) + '/sinatra/lib'
+require 'sinatra'
+
+Sinatra::Application.default_options.merge!(
+ :run => false,
+ :env => :production
+)
+
+require 'application'
+run Sinatra.application
5 app/test/fixtures/001-test-article/test-article.txt
@@ -0,0 +1,5 @@
+# This is the test article
+
+This is the perex of the test article
+
+Lorem ipsum dolor sit amet, **consectetur** adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
63 app/test/marley_test.rb
@@ -0,0 +1,63 @@
+require 'rubygems'
+require 'rack'
+require 'sinatra'
+require 'sinatra/test/unit'
+
+# Redefine data directory for tests
+module Blog
+ DATA_DIRECTORY = File.join(File.dirname(__FILE__), 'fixtures')
+end
+
+# Require application file
+require '../marley'
+
+# Redefine database with comments for tests
+module Blog
+ class Comment < ActiveRecord::Base
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => './fixtures/test.db' )
+ end
+end
+
+# Setup fresh comments table
+File.delete('./fixtures/test.db') if File.exists?('./fixtures/test.db')
+ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => './fixtures/test.db')
+load File.join( '..', '..', 'config', 'db_create_comments.rb' )
+
+
+class MarleyTest < Test::Unit::TestCase
+
+ configure do
+ set_options :views => File.join( File.dirname(__FILE__), '..', 'views' )
+ end
+
+ def test_should_show_index_page
+ get_it '/'
+ assert @response.status == 200
+ end
+
+ def test_should_show_article_page
+ get_it '/test-article.html'
+ # puts @response.inspect
+ assert @response.status == 200
+ assert @response.body =~ /<h1>This is the test article<\/h1>/
+ end
+
+ def test_should_send_404
+ get_it '/i-am-not-here.html'
+ assert @response.status == 404
+ end
+
+ def test_should_create_comment
+ comment_count = Blog::Comment.count
+ post_it '/test-article/comments', { :ip => "127.0.0.1",
+ :user_agent => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_4; en-us)",
+ :body => "Testing comments...",
+ :post_id => "test-article",
+ :url => "",
+ :author => 'Tester',
+ :email => "tester@localhost" }
+ assert @response.status == 302
+ assert Blog::Comment.count == comment_count + 1
+ end
+
+end
14 app/views/index.erb
@@ -0,0 +1,14 @@
+ <h1>All posts <small>(<%= @posts.size %>)</small></h1>
+
+ <ul class="posts">
+ <% @posts.each do |post| %>
+ <li>
+ <small><%= human_date post.published_on %> &mdash;</small>
+ <strong><a href="/<%= post.id %>.html"><%= post.title %></a></strong>
+ <span class="perex"><%= post.perex %></span>
+ <span><%= post.comments.size %> comments</span>
+ </li>
+ <% end %>
+ </ul>
+
+ <pre><%= @posts.inspect.tr('<', '&lt;').tr('>', '&gt;') %></pre>
27 app/views/layout.erb
@@ -0,0 +1,27 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+ <title><%= h(@page_title) %></title>
+
+ <style type="text/css" media="screen">
+ body { font-family: sans-serif; font-size: 90%; padding: 2em; }
+ pre { color: #444; font-size: 110%; white-space: pre-wrap; white-space: -moz-pre-wrap !important; padding: 1em; border: 4px solid #fafafa;}
+ ul { margin: 0; padding: 0; }
+ li { list-style-type: none; margin-bottom: 1em; }
+ li span.perex { font-size: 80%; color: #444; }
+ span.error { color: white; background-color: red; margin: 0 0 0.2em 0; padding: 0.3em; }
+ </style>
+
+</head>
+
+<body>
+
+ <%= yield %>
+
+
+</body>
+</html>
53 app/views/post.erb
@@ -0,0 +1,53 @@
+ <a href="/">Index</a>
+
+ <hr />
+
+ <h1><%=h @post.title %></h1>
+
+ <div id="meta">
+ Published
+ <%= "in categories <em>#{@post.categories.join(', ')}</em> " if @post.categories %>
+ on <%= human_date @post.published_on %>
+ </div><!-- /meta -->
+
+ <%= @post.body_html %>
+
+ <hr />
+
+ <% unless @post.comments.empty? %>
+ <h2 id="comments">Comments</h2>
+
+ <div class="comments">
+ <% @post.comments.each do |comment| %>
+ <p id="comment_<%= comment.id %>" style="<%= 'color:red' if comment.spam? %>"><%=h comment.author %> said on <%= human_date(comment.created_at) %>: <%=h comment.body %></p>
+ <% end %>
+ </div><!-- /comments -->
+
+ <hr />
+ <% end %>
+
+ <h2 id="add_comment">Add Comment</h2>
+
+ <form action="/<%= @post.id %>/comments#add_comment" method="post" accept-charset="utf-8">
+
+ <label>Author:</label><br />
+ <input type="text" name="author" value="" id="author" />
+ <% if @comment and @comment.errors %><span class="error">Author <%= @comment.errors.on(:author).first %></span><% end %>
+ <br />
+ <label>E-mail:</label><br />
+ <input type="text" name="email" value="" id="email" />
+ <% if @comment and @comment.errors %><span class="error">E-Mail <%= @comment.errors.on(:email).first %></span><% end %>
+ <br />
+ <label>URL:</label><br />
+ <input type="text" name="url" value="" id="url" />
+ <br />
+ <label>Message:</label><br />
+ <textarea name="body" rows="8" cols="40"></textarea>
+ <% if @comment and @comment.errors %><span class="error">Message <%= @comment.errors.on(:body).first if @comment %></span><% end %>
+ <br />
+
+ <p><input type="submit" value="Post &rarr;" accesskey="s"></p>
+ </form>
+
+
+<pre> <%= request.env.inspect %></pre>
8 config/config.example.yml
@@ -0,0 +1,8 @@
+blog:
+ title: "{REPLACE WITH TITLE OF YOUR BLOG}" # Displayed as <title> on index page
+ name: "{REPLACE WITH SHORT NAME OF YOUR BLOG}" # Displayed as <title> on article page
+
+# See http://soakedandsoaped.com/articles/2006/10/01/how-to-protect-a-rails-application-against-spam-with-akismet
+akismet:
+ key: "{REPLACE WITH YOUR AKISMET KEY}"
+ url: "{REPLACE WITH YOUR AKISMET URL}"
12 config/db_create_comments.rb
@@ -0,0 +1,12 @@
+# Inspired by Ryan Tomayko, http://github.com/rtomayko/wink/tree/master/lib/wink/models.rb#L276
+ActiveRecord::Schema.define(:version => 1) do
+ create_table :comments do |t|
+ t.string :post_id, :null => false
+ t.string :author, :email, :url, :ip, :referrer , :user_agent
+ t.text :body
+ t.datetime :created_at, :default => 'NOW()'
+ t.boolean :checked, :default => false
+ t.boolean :spam, :default => false
+ end
+ add_index :comments, :post_id
+end
79 config/deploy.example.rb
@@ -0,0 +1,79 @@
+# ----- Setup SSH -------------------------------------------------------------
+set :user, "{REPLACE WITH YOUR SSH USERNAME}"
+# -----------------------------------------------------------
+# set :password, "REPLACE WITH YOUR PASSWORD or USE SSH KEYS"
+# -----------------------------------------------------------
+# ssh_options[:port] = {SET THIS IF YOU USE NON STANDARD PORT}
+
+
+# ----- Setup Git -------------------------------------------------------------
+set :runner, "deployer"
+set :application, 'app'
+set :scm, :git
+# set :branch, "deploy"
+set :git_enable_submodules, 1
+set :repository, "{REPLACE WITH YOUR REPOSITORY}"
+set :deploy_via, :remote_cache
+set :deploy_to, "{REPLACE WITH YOUR PATH}/#{application}"
+set :use_sudo, false
+
+# ----- Setup servers, paths and callbacks ------------------------------------
+role :app, "{REPLACE WITH YOUR SERVER}"
+role :web, "{REPLACE WITH YOUR SERVER}"
+role :db, "{REPLACE WITH YOUR SERVER}", :primary => true
+
+# ----- Specific tasks --------------------------------------------------------
+
+namespace :blog do
+ task :synchronize, :roles => :app do
+ upload "../data", "#{deploy_to}/../data"
+ end
+end
+
+# ----- Run these commands after each deploy ----------------------------------
+task :after_update_code, :roles => [:app, :db] do
+ # run "ln -nfs #{shared_path}/sinatra #{release_path}/sinatra"
+ run "ln -nfs #{deploy_to}/../data #{release_path}/data"
+end
+
+# ----- Over-ride deploy tasks ------------------------------------------------
+
+namespace :deploy do
+
+ desc "Deploy new version of application on server"
+ task :default, :roles => :app do
+ transaction do
+ stop
+ update
+ start
+ end
+ end
+
+ desc "Deploy new application on server"
+ task :cold do
+ update
+ start
+ end
+
+ desc "Return to previous version"
+ task :rollback do
+ stop
+ rollback_code
+ start
+ end
+
+ desc "Restart the webserver"
+ task :restart, :roles => :app do
+ run "cd #{current_path}; rake server:restart"
+ end
+
+ desc "Start the webserver"
+ task :start, :roles => :app do
+ run "cd #{current_path}; rake server:start"
+ end
+
+ desc "Stop the webserver"
+ task :stop, :roles => :app do
+ run "cd #{current_path}; rake server:stop"
+ end
+end
11 data/001-this-is-the-first-post/this-is-first-post-and-the-filename-doesnt-matter-actually.txt
@@ -0,0 +1,11 @@
+{{
+categories:
+ - rails
+ - webdesign
+}}
+
+# Sinatra.rb or the easiest route from PHP to real Ruby
+
+First paragraph of an article is the perex displayed on homapage and not displayed on article page
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
5 data/002-this-is-the-second-post-and-it-is-a-draft.draft/this-is-a-second-still-unpublished-post.txt
@@ -0,0 +1,5 @@
+# This second article is a draft
+
+Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
117 data/003-this-is-the-third-article/third.txt
@@ -0,0 +1,117 @@
+# This article contains some 20KBs of real-world Markdown to put some stress on RDiscount :)
+
+Which RDiscount just eats for breakfast in some 0.02 secs or so
+
+Framework pro vývoj webových aplikací a stránek [Ruby on Rails](http://www.rubyonrails.org) si za poslední dva tři roky získal **takovou** slávu, že jedni jsou nadšeni zcela nad míru běžně obvyklou a kážou Rails kudy chodí, druzí mluví o „pouhé módě“, „skvělém marketingu 37 Signals“ a „zbytečném *hype*“.
+
+Z pohledu na významné vývojářské [blogy](http://www.37signals.com/svn/ "Signal vs. Noise") či [weby](http://promosearch.atomz.com/search/promosearch?sp-a=sp1000a5a9&sp-f=ISO-8859-1&sp-t=general&sp-k=Articles%7CBooks%7CConferences%7COther%7CWeblogs&sp-x-1=cat&sp-p-1=phrase&sp-q-1=&sp-x-2=cat2&sp-q-2=&sp-c=25&sp-p=all&c=&p=&query=ruby+on+rails&sp-q=ruby+on+rails&search=Go "O'Reilly Network") nebo [videa, kde v patnácti minutách naprogramujete kompletní blog](http://media.rubyonrails.org/video/rails_take2_with_sound.mov "Video, QuickTime 52MB") člověk rozhodně získá dojem, že Rails jsou mýtická [„stříbrná kulka“ softwarového vývoje](http://en.wikipedia.org/wiki/No_Silver_Bullet), technologie, jejíž pouhé použití či nasazení několikanásobně zvyšuje produktivitu. A k dovršení všeho na Ruby on Rails běží nejslavnější aplikace a stránky současného webu: [Basecamp](http://basecamphq.com/), [Shopify](http://shopify.com/), [Cork'd](http://corkd.com/), [A List Apart](http://alistapart.com/) či [Twitter](http://twitter.com/).
+
+Není třeba si tedy klást (řečnické) otázky jako [„Je Ruby on Rails stříbrná kulka?“](http://about.82ask.com/2007/04/05/ruby-on-rails/) a podobně, ale můžeme rovnou vyložit karty. Řekněme to narovinu:
+
+<p class="highlight"><strong>Jestliže se zabýváte vývojem pro web, Ruby on Rails je to nejzajímavější, co se za poslední dva roky na tomto poli objevilo. Rails je <em>killer app</em> webového vývoje.</strong></p>
+
+Panebože! Slyším ty výkřiky! Ne Ajax? Ne PHP 5? Ne Script.aculo.us? Ne … Zend framework? Ne Adobe Apollo? Ne. A nejenom proto, že Rails v sobě Ajax i Script.aculo.us zahrnují (a v jistém smyslu, k němuž se vrátíme, teprve akcelerují, neboli „staví na koleje“).
+
+Rails jsou jedinečné (a v tomto smyslu „nejzajímavější“) proto, že kromě mnoha ryze technických konceptů a inovací vnesly do webového vývoje zásadní **intelektuální koncepty** a nové myšlenky. To je ohled, který v debatách ohledně Ruby on Rails pravidelně zapadá. Debaty ohledně Rails totiž trpí známým syndromem **„to není nic nového pod sluncem“**. Poučná je v tomto smyslu debata k článku [„Proč bylo Ruby on Rails možné napsat pouze a jedině v Ruby?“](http://www.oreillynet.com/ruby/blog/2007/03/why_was_rails_only_possible_wi.html "O'Reilly Ruby"). V ní se diskutující pravidelně vracejí k objevné myšlence, že „to samé“ je možné udělat v Pythonu nebo v Perlu, že Model-View-Controller není nic nového, že lepší než Rails je framework [Django](http://www.djangoproject.com/) pro Python nebo [Catalyst](http://www.catalystframework.org/) pro Perl. A další variace: to umí .NET/Java/atd. už dávno a líp, a navíc mají IDE, a striktní typování. PHP je rozšířenější, a rychlejší, a levnější na provoz. A s „free-hostingem“. Jenže na webu se nyní o ničem nemluví s takovým nadšením jako o Rails, a jasná volba pro všechny obdivované _start-upy_ není ani .NET, ani Python. Ani PHP.
+
+Tato diskuse totiž posuzuje Rails z omezeně **technologického pohledu**. A z omezeně technologického pohledu je Ruby on Rails skutečně **jen další MVC webový framework**. Co však Rails činí zajímavé a jedinečné jsou netechnické (ve striktním slova smyslu), intelektuální a sociální aspekty, kterými se chci zabývat v těchto článcích. Právě ty ve vývojářích vyvolávají pocit „všechno v Rails a už nikdy jinak“ a právě ty vyvolaly mohutnou vlnu nadšení a nové energie pro vývoj na webu. Postoj „všechno už tu bylo, všechno je pořád stejné, je to prostě práce“ jistě v nikom příliš [entuziasmu nevzbudí](http://www.bitwisemag.com/2/What-s-Wrong-With-Ruby "I don’t want someone chatting away to me and telling me how "cool" it all is (I’ve lived long enough as a computer programmer to know it’ll never really be "cool" to be one). I just want the straight facts, plainly put."). Nejprve však krátký úvod či opakování.
+
+
+Co je Ruby a co je Ruby on Rails
+--------------------------------
+
+Ruby on Rails je **vývojový framework** postavený na bázi vzoru [Model-View-Controller](http://en.wikipedia.org/wiki/Model-view-controller) (podobně jako za všechny např. [Apache Struts](http://struts.apache.org/) či klon Rails pro PHP [CakePHP](http://www.cakephp.org/)), který automaticky mapuje URL na vnitřní řídící prvky aplikace, abstrahuje přístup k datům v databázi pomocí [Object-relational mapping](http://en.wikipedia.org/wiki/Object-relational_mapping) („řádky“ v databázi se převedou na instance objektů, „sloupce“ na jejich atributy) a obsahuje rozsáhlé pomocné knihovny pro snadné generování HTML, práci s Ajaxem, formátování dat a další. „Naučit se Ruby on Rails“ tedy znamená seznámit se s konvencemi a principy, na nichž je postavené („kde jsou šablony?“, „jak zpracuji data z formuláře?“) a naučit se programovací jazyk, který Rails používají: Ruby. Protože Ruby je základem frameworku Ruby on Rails, a jeho koncepce Rails zásadně ovlivnila, zastavíme se nejprve u něj.
+
+[Ruby](http://www.ruby-lang.org), tedy **programovací jazyk** samotný, v němž jsou Rails napsány a v němž píšete Rails aplikace, je relativně nový jazyk, který vytvořil Yukihiro Matsumoto („Matz“), a který vychází ze základního principu: programování je **tvořivá činnost**, která vám má přinášet radost:
+
+<blockquote>
+„Věřím, že — alespoň do jisté míry — je smyslem života být šťastný. Na základě tohoto přesvědčení je Ruby navrženo tak, že je nejenom snadné, ale i zábavné v něm programovat. Ruby vám umožňuje soustředit se na kreativní stránku programování, a nepřidělává vám další starosti.“
+<p class="author"><span class="tilde">~</span> Yukihiro Matsumoto<span class="source">, předmluva k prvnímu vydání knihy <a href="http://www.rubycentral.com/book/foreword.html">Programming Ruby</a></span></p>
+</blockquote>
+
+Podobnou větu v předmluvě k referenční příručce programovacího jazyka by pravděpodobně čekal málokdo. Ostatní principy Ruby jsou však z tohoto základního principu odvozené — přestože se podobné uvažování o softwarovém vývoji může zdát _hardcore geekům_ trochu divné. Výchozí motivací Matsumota bylo vytvořit jazyk, s nímž bude **radost pracovat** a který dá programátorovi co nejkomfortnější **vyjadřovací prostředky**. Fakt, že to znamená dokonale objektově orientovaný jazyk, perfektně integrované regulární výrazy, snadno čitelnou, úspornou syntaxi a pohodlné iterátory typu `10.times {puts "ahoj"}`, je přitom zcela vedlejší. Matsumoto se totiž přímo [odvolává](http://cs.byu.edu/colloquia_files/2006Fall/presentations/Matz_slides/mgp00011.html) na hypotézu [**jazykového relativismu**](http://cs.wikipedia.org/wiki/Jazykový_relativismus), která tvrdí, že vnímání světa a způsob myšlení je radikálním způsobem ovlivněno právě vyjadřovacími prostředky, tedy **jazykem**, kterým mluvíme. Tato hypotéza tvrdí nejenom to, že v různých jazycích myslíme různým způsobem. Ve své radikální podobě tato hypotéza v teorii Ludwiga Wittgensteina tvrdí, že **to, co nemohu v jazyce vyjádřit, nemohu ani myslet** (_Filosofická zkoumání_, <span class="note">§243 a následující</span>).
+
+Matsumoto tuto hypotézu vztahuje i na jazyky zvané **programovací**: programovací jazyk je totiž specifickým případem umělého jazyka, který má syntaxi, gramatiku i slovní zásobu, stejně jako jazyky přirozené. Vykládá ji tak, že čím pohodlněji se mohu **vyjádřit**, tím pohodlněji mohu i **myslet** — a v programování jde pochopitelně především o myšlení, o „výsledek“: o to, co má program dělat („nahradit všechny výrazy XXX v řetězci za YYY“, „vypsat všechny články z databáze publikované tento měsíc“, atd.). Syntaxe, gramatika, slovní zásoba, to jsou vyjadřovací prostředky, které usnadňují či znesnadňují vyjádření samotné — nejsou však vůči vyjadřované „myšlence“ nijak sekundární. Napsat program, který nahradí výraz XXX za YYY lze stejně tak v Javě, PHP, Ruby nebo assembleru, ale **pohodlnost**, s jakou tuto (triviální) myšlenku v jednotlivých jazycích vyjádříte, bude dost rozdílná. „V assembleru můžete napsat všechno. Nikdo ale už nechce psát v assembleru“ ([Yukihiro Matsumoto, _The Philosophy of Ruby_](http://www.artima.com/intv/ruby.html)).
+
+<span class="note">*Upozornění!* Následující text srovnává syntaxi Ruby a PHP. Účelem srovnání **není** dokazovat, která syntaxe je „lepší“. Účelem je demonstrovat filosofii, na které je Ruby založena. Možná se vám tato filosofie bude zdát mylná, nepodstatná nebo lhostejná. Možná se vám bude zdát, že to je „stejně jedno“, že je jazyk jako jazyk a navíc je důležitější striktní typování, výkonnostní parametry nebo paletka _Změnit všechny výskyty…_ ve „vašem IDE“. Pak vás následující argumentace asi nepřesvědčí.</span>
+
+Právě určitá **expresivita** jazyka, tedy snadnost, s jakou vyjadřuje myšlenky (programátora) je základní vlastností a rozlišovacím znakem Ruby. Rozdíl mezi „příkazem“, tak jak si jej v duchu řekneme, a skutečným kódem je v Ruby minimální. Srovnejte následující úryvek kódu v PHP, důvěrně známý všem webovým programátorům:
+
+ if ( !empty($this->email) ) echo $this->email;
+
+Stejný úryvek v Ruby, resp. v [rozšíření Rails](http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/core_ext/blank.rb ):
+
+ print self.email unless self.email.blank?
+
+Srovnání není určeno k posouzení toho, který jazyk je „lepší“, protože to vždy znamená otázku „lepší k čemu?“ nebo „lepší pro koho?“ a ty si zde neklademe, protože si je pokládá každý sám za sebe. Oba zápisy jsou si velmi podobné — na rovině syntaxe však první příklad připomíná spíše **rovnici** (matematický symbol negace, závorky), zatímco příklad druhý vypadá jako jednoduchá **věta** v přirozeném jazyce: „Vypiš proměnnou, pokud není prázdná“. _Print something to screen, unless that something is blank_ — dokonce se správným slovosledem. Podmínka `unless` zde slouží jako modifikátor (_modifier_) příkazu `print`. To, který způsob zápisu se vám zdá přehlednější, záleží zčásti na tom, zda myslíte spíše „v rovnici“, nebo „ve větě“, a především na tom, na co jste zvyklí. Díky síle zvyku se často syntaxe Ruby zdá na první pohled „divná“. Ale při řešení abstraktních problémů je přeci jen většina z nás zvyklá přemýšlet „ve větách“, dal by se shrnout přístup Ruby.
+
+„Vypiš proměnnou, pokud není prázdná“ je velmi, velmi triviální myšlenka. Tuto triviální myšlenku můžeme vyjádřit buď složitým, nebo jednoduchým zápisem. Symbol negace, striktní pravidla pro použití závorek — to vše je trivialitě oné myšlenky v zásadě cizí. V tom je tento příklad příznačný: programátoři webových aplikací velmi často na podobnou kontrolu hodnoty proměnné nedbají. Je otázka, zda a nakolik v tom má prsty přílišná striktnost syntaxe PHP. (Je přitom jisté, že chyby v nesprávně uzavřených nebo překřížených závorkách jsou v PHP jedny z nejčastějších.)
+
+Vezměme si však jiný příklad, převzatý z nejoriginálnější učebnice jazyka Ruby, [Why's Poignant Guide to Ruby](http://poignantguide.net/ruby/):
+
+ 5.times { print "Hurá! " }
+ => ‚Hurá! Hurá! Hurá! Hurá! Hurá!‘
+
+_Five times print "Hurá"_, neboli „pětkrát napiš ‚Hurá!‘“ je opět věta v přirozeném jazyce, v angličtině, a opět se správným slovosledem. Vidíme, že zahrnuje „matematické znaky“ (složené uvozovky), ty ale slouží víceméně jako interpunkce — například pomlčky nebo čárky --, pro oddělení částí souvětí.
+
+Tato „literární povaha“ je pro Ruby zcela zásadní a velmi ovlivnila právě framework Ruby on Rails. Jak [říká](http://podcast.rubyonrails.org/programs/1/episodes/dan_benjamin "Audio MP3") vývojář [Dan Benjamin](http://hivelogic.com/): „Ruby vám umožňuje se skvěle vyjádřit, a nemusíte u toho psát tucty řádků komenářů — kód v Ruby je srozumitelný sám od sebe“. I programátor, který Ruby neovládá, velmi dobře porozumí kódu, jako je:
+
+ print ["banán", "citron", "ananas"].sort.last.capitalize
+ => ‚Citron‘
+
+„Porozumí“ přitom znamená: „odhadne výstup programu“, porozumí tomu, co program udělá (tedy oné „myšlence“), nikoliv nutně syntaxi. Důležité je, že uvedenému kousku kódu porozumí i neprogramátor se základní znalostí angličtiny, na rozdíl od identického kódu např. v PHP:
+
+ $a = Array("banán", "citron", "ananas");
+ sort($a);
+ print ucwords( end($a) );
+ => ‚Citron‘
+
+Z tohoto důvodu je Ruby oblíbeným a často používaným programovacím jazykem pro [výuku programování](http://pine.fm/LearnToProgram/). Jak [říká](http://podcast.rubyonrails.org/programs/1/episodes/chris_pine "Audio MP3") autor knihy [Learn to Program](http://www.pragmaticprogrammer.com/titles/fr_ltp/), Chris Pine, Ruby je ideálním jazykem pro výuku programování, protože psát v něm programy je za á snadné a za bé je to zábava. (Úplně jinou kapitolou v použití Ruby pro výuku programování je projekt Ruby artisty Why The Lucky Stiffa [**Hackety Hack**](http://hacketyhack.net/), k němuž se zajisté ještě vrátíme.)
+
+Na uvedených úryvcích kódu je ihned vidět jedna důležitá vlastnost Ruby: **řetězení** (_chainability_). Metody, které jsou volány na objektu (v tomto případě poli), vrací objekt samotný, takže lze volání metod řetězit a předávat si jej mezi nimi. <span class="note">(Řetězení prostřednictvím Rails ovlivnilo mnoho dalších projektů, typicky např. JavaScript frameworky [Prototype.js](http://encytemedia.com/blog/articles/2006/08/30/a-flurry-of-prototype-updates), [JQuery](http://docs.jquery.com/How_jQuery_Works#Chainability_.28The_Magic_of_jQuery.29) nebo [Fry](http://dev.april-child.com/fry/test/test-ac.fry.html).)</p> Řetězení se neuplatňuje jen v případě triviálních operací jako je seřazení pole, ale i v daleko sofistikovanějších výstupech (s výjimkou přetypovací metody `to_a`, neboli _to array_ je uvedený kód na první pohled srozumitelný):
+
+ # http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTP.html
+ require 'net/http'
+ print Net::HTTP.get('www.google.cz', '/').match("<title>(.*)<\/title>").to_a.last
+ => ‚Google‘
+
+Podobně jako podmínkové modifikátory (`print variable unless variable.blank?`) je řetězení silnou zbraní Ruby, protože umožňuje psát velmi **čitelný** a **úsporný** kód: jak říká známý teorém o jazyce [Python](http://www.paulgraham.com/power.html), „V úspornosti je síla“ (_Succinctness is power_). Nebo, jak praví známý aforismus, počítačový kód má být v první řadě čitelný pro lidi, a jen mimochodem též pro stroje.
+
+Proč je tomu tak? Jak [říká](http://www.itconversations.com/shows/detail1638.html "Audio MP3, 21. minuta") Matz: <cite>Úsporný kód znamená méně chyb — čím méně řádků kódu, tím méně chyb. A méně chyb znamená, že se cítíte chytřejší.</cite>
+
+To je to, co Matz v uvedené přednášce nazývá „efektivita měřená počtem úderů do klávesnice“ (_efficiency in keystrokes_) — nikoliv efektivita z hlediska výpočetního výkonu, ale z hlediska produktivity. Úspornost a čitelnost kódu, snadno zapamatovatelná syntaxe bez obtížné „interpunkce“ — to vše slouží k tomu, aby se programátor mohl **soustředit na „myšlenku“ a ne na to, jak ji vyjádřit**. Lze říci, že všechny ostatní, ryze technické vlastnosti Ruby jsou podřízeny tomuto principu. Kruh se uzavírá:
+
+<blockquote>
+ Programujeme proto, že nás to baví. I když programujeme pro peníze, stejně chceme, aby nás to bavilo.
+ <p class="author"><span class="tilde">~</span> Yukihiro Matsumoto<span class="source">, přednáška na <a href="http://cs.byu.edu/colloquia_files/2006Fall/presentations/Matz_slides/mgp00031.html">Brigham Young University</a></span></p>
+</blockquote>
+
+Proč tak rozsáhlý úvod do Ruby v článku, který má Ruby on Rails ve svém názvu? Protože základní principy Ruby zde shrnuté ovlivnily návrh celého frameworku Rails (ať již se jedná o princip „konvence má přednost před konfigurací“, o využití známých návrhových vzorů jako je [Active Record](http://en.wikipedia.org/wiki/ActiveRecord), a další). Takže až potkáme podobný úryvek kódu:
+
+ # http://api.rubyonrails.com/classes/ActiveSupport/CoreExtensions/Numeric/Time.html#M000394
+ 10.minutes.ago
+ => Sat May 26 19:52:58 +0200 2007
+
+nebo:
+
+ # http://api.rubyonrails.com/classes/ActionView/Helpers/DateHelper.html#M000575
+ t = Article.find_by_id(1).published_at
+ => Sat May 26 19:02:12 +0200 2007
+ time_ago_in_words(t)
+ => ‚about one hour‘
+
+budeme přesně vědět, odkud vítr vane. Popularita Ruby skokově vzrostla právě díky Ruby on Rails — jak říká samotný Matz: [„Rails jsou pro Ruby _killer app_“](http://www.slideshare.net/vishnu/the-top-10-reasons-the-ruby-programming-language-sucks/). Rails samotné se pak staly populárními díky tomu, že se přesně trefily do neuspokojivé situace na poli vývoje pro web, kterému vládlo (a vládne) PHP, se svými světlými i temnými stránkami, s nedostatečně ukotvenými pravidly vývoje, kdy každý vývojář objevuje Ameriku. Vzbudily v komunitě vývojářů nadšení, protože usnadnily práci s [Ajaxem](http://www.prototypejs.org/) a [vizuálními efekty](http://script.aculo.us/) a umožnily vývojářům soustředit se konečně na to, <strong>co</strong> chtějí udělat, než na to, <strong>jak</strong> toho dosáhnout. Co je ale na Rails tak fascinujícího, že o málokteré technologii se mluví s takovým nadšením? Proč prodeje knih o Ruby on Rails rostou na angloamerickém trhu [nejrychleji z celé oblasti web designu](http://radar.oreilly.com/archives/2007/05/state_of_the_co_6.html)? Právě tomu se bude věnovat další článek.
+
+
+Poznámky a odkazy:
+------------------
+
+* Všechny uvedené příklady Ruby si můžete vyzkoušet ve webové verzi konzole: [http://tryruby.hobix.com/](http://tryruby.hobix.com/).
+
+* První vydání základní referenční příručky k Ruby, _The Pickaxe Book_, je přístupné online: [http://www.ruby-doc.org/docs/ProgrammingRuby/](http://www.ruby-doc.org/docs/ProgrammingRuby/)
+
+* Pro neředěný zážitek z Ruby a s Ruby ovšem doporučuji dobrodružný cestopis _Why's (Poignant) Guide to Ruby_: [http://www.poignantguide.net/](http://www.poignantguide.net/)
+
+* Pro další článek nemusíte znát nic více než ono [video, které všechno začalo](http://media.rubyonrails.org/video/rails_take2_with_sound.mov) <span class="note">(alespoň v mém případě)</span>
+
+<span class="note">Poděkování patří Vítku Burdovi, Vráťovi Čermákovi, Petru Krontorádovi, Pavlu Šimkovi a Václavu Vančurovi za cenné podněty, připomínky, rady a opravy při přípravě článku.</span>
5 data/004-fourth-article/004.txt
@@ -0,0 +1,5 @@
+# This is the fourth article
+
+Just testing `rake blog:sync`
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
BIN  data/development.db
Binary file not shown
140 vendor/akismet.rb
@@ -0,0 +1,140 @@
+# Akismet
+#
+# Author:: David Czarnecki
+# Copyright:: Copyright (c) 2005 - David Czarnecki
+# License:: BSD
+class Akismet
+
+ require 'net/HTTP'
+ require 'uri'
+
+ STANDARD_HEADERS = {
+ 'User-Agent' => 'Akismet Ruby API/1.0',
+ 'Content-Type' => 'application/x-www-form-urlencoded'
+ }
+
+ # Instance variables
+ @apiKey
+ @blog
+ @verifiedKey
+ @proxyPort = nil
+ @proxyHost = nil
+
+ # Create a new instance of the Akismet class
+ #
+ # apiKey
+ # Your Akismet API key
+ # blog
+ # The blog associated with your api key
+ def initialize(apiKey, blog)
+ @apiKey = apiKey
+ @blog = blog
+ @verifiedKey = false
+ end
+
+ # Set proxy information
+ #
+ # proxyHost
+ # Hostname for the proxy to use
+ # proxyPort
+ # Port for the proxy
+ def setProxy(proxyHost, proxyPort)
+ @proxyPort = proxyPort
+ @proxyHost = proxyHost
+ end
+
+ # Call to check and verify your API key. You may then call the #hasVerifiedKey method to see if your key has been validated.
+ def verifyAPIKey()
+ http = Net::HTTP.new('rest.akismet.com', 80, @proxyHost, @proxyPort)
+ path = '/1.1/verify-key'
+
+ data="key=#{@apiKey}&blog=#{@blog}"
+
+ resp, data = http.post(path, data, STANDARD_HEADERS)
+ @verifiedKey = (data == "valid")
+ end
+
+ # Returns <tt>true</tt> if the API key has been verified, <tt>false</tt> otherwise
+ def hasVerifiedKey()
+ return @verifiedKey
+ end
+
+ # Internal call to Akismet. Prepares the data for posting to the Akismet service.
+ #
+ # akismet_function
+ # The Akismet function that should be called
+ # user_ip (required)
+ # IP address of the comment submitter.
+ # user_agent (required)
+ # User agent information.
+ # referrer (note spelling)
+ # The content of the HTTP_REFERER header should be sent here.
+ # permalink
+ # The permanent location of the entry the comment was submitted to.
+ # comment_type
+ # May be blank, comment, trackback, pingback, or a made up value like "registration".
+ # comment_author
+ # Submitted name with the comment
+ # comment_author_email
+ # Submitted email address
+ # comment_author_url
+ # Commenter URL.
+ # comment_content
+ # The content that was submitted.
+ # Other server enviroment variables
+ # In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible.
+ def callAkismet(akismet_function, user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+ http = Net::HTTP.new("#{@apiKey}.rest.akismet.com", 80, @proxyHost, @proxyPort)
+ path = "/1.1/#{akismet_function}"
+
+ data = "user_ip=#{user_ip}&user_agent=#{user_agent}&referrer=#{referrer}&permalink=#{permalink}&comment_type=#{comment_type}&comment_author=#{comment_author}&comment_author_email=#{comment_author_email}&comment_author_url=#{comment_author_url}&comment_content=#{comment_content}"
+ if (other != nil)
+ other.each_pair {|key, value| data.concat("&#{key}=#{value}")}
+ end
+ data = URI.escape(data)
+
+ resp, data = http.post(path, data, STANDARD_HEADERS)
+
+ return (data != "false")
+ end
+
+ protected :callAkismet
+
+ # This is basically the core of everything. This call takes a number of arguments and characteristics about the submitted content and then returns a thumbs up or thumbs down. Almost everything is optional, but performance can drop dramatically if you exclude certain elements.
+ #
+ # user_ip (required)
+ # IP address of the comment submitter.
+ # user_agent (required)
+ # User agent information.
+ # referrer (note spelling)
+ # The content of the HTTP_REFERER header should be sent here.
+ # permalink
+ # The permanent location of the entry the comment was submitted to.
+ # comment_type
+ # May be blank, comment, trackback, pingback, or a made up value like "registration".
+ # comment_author
+ # Submitted name with the comment
+ # comment_author_email
+ # Submitted email address
+ # comment_author_url
+ # Commenter URL.
+ # comment_content
+ # The content that was submitted.
+ # Other server enviroment variables
+ # In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible.
+ def commentCheck(user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+ return callAkismet('comment-check', user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+ end
+
+ # This call is for submitting comments that weren't marked as spam but should have been. It takes identical arguments as comment check.
+ # The call parameters are the same as for the #commentCheck method.
+ def submitSpam(user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+ callAkismet('submit-spam', user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+ end
+
+ # This call is intended for the marking of false positives, things that were incorrectly marked as spam. It takes identical arguments as comment check and submit spam.
+ # The call parameters are the same as for the #commentCheck method.
+ def submitHam(user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+ callAkismet('submit-ham', user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.