Простой способ сделать скриншот с видео при загрузке

Posted on February 12, 2010
При загрузке видео необходимо сделать превьюшку картинки, чтоб плеер ее показывал. Для этого я написал простой код:


 before_save :define_screenshot
 
 def define_screenshot
    return if file.queued_for_write.empty?
    temp_name = get_temp_name
    video_file = file.queued_for_write[:original].path
    cmd = "ffmpeg -i #{video_file} -an -ss 00:00:10 -an -r 1 -vframes 1 -y -f mjpeg #{temp_name} >/dev/null 2>&1"
    Rails.logger.info "Running cmd: #{cmd}"
    system cmd
    self.thumbnail = File.open(temp_name)
  end

  def get_temp_name
    tempdir = Dir::tmpdir || '/tmp'
    t = Time.now.strftime("%Y%m%d")
    path = "clip#{t}-#{$$}-#{rand(0x100000000).to_s(36)}.jpg"
    File.join(tempdir, path)
  end


использование Google Docs в приложении

Posted on August 25, 2009
Недавно мне понадобилось в один сервис вставить несколько страниц текста, поскольку все эти тексты были уже в виде google docs, то я написал простой класс который их получает и показывает:

require 'hpricot'

class Gdoc
  def self.get id
    text = Rails.cache.fetch("doc-#{id}", :expires_in => 15.minutes) do
      url = URI.parse('http://docs.google.com')
      res = Net::HTTP.start(url.host, url.port) do |http|
        http.get("/Doc?id=#{id}")
      end
      text = res.body
      doc = Hpricot(text)
      body = (doc/'body')
      body.search("#google-view-footer").remove
      body.search("script").remove
      body.inner_html.gsub('File?id=', 'http://docs.google.com/File?id=')
    end
    text
  end

  def self.index
    docs = Rails.cache.fetch("docs", :expires_in => 15.minutes) do
      raw = self.get GDOC_INDEX
      doc = Hpricot(raw)
      docs = []
      doc.search('//a').each do |link|
        docs << {:id => link['href'].gsub('View?docid=',''), :name => link.innerText, :path => link.innerText.downcase.gsub(' ','-')}
      end
      docs
    end
    docs
  end
end
Комментарии, думаю, излишни.

Создание баннерной системы на rails часть 1.

Posted on March 19, 2009
В связи с выходом нового rails 2.3.2 появилось множество возможностей ускорить отклик приложения.
Одним из таких способов является Rails Metal. Благодаря нему возможно обрабатывать запросы в обход всей машинерии, что дает огромное преимущество в скорости работы.
Для примера я решил создать приложение для баннерной сети. Приложение крайне просто и используется только как пример.
Вначале создадим новое приложение с помощью шаблона:

git :init

plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true
plugin 'haml', :git => "git://github.com/nex3/haml.git", :submodule => true
plugin 'paperclip', :git => "git://github.com/thoughtbot/paperclip.git", :submodule => true
plugin 'acts_as_taggable_redux', :git => 'git://github.com/geemus/acts_as_taggable_redux.git', :submodule => true
plugin 'will_paginate', :git => 'git://github.com/mislav/will_paginate.git', :submodule => true
plugin 'role_requirement', :git => 'git://github.com/timcharper/role_requirement.git', :submodule => true


run "touch tmp/.gitignore log/.gitignore vendor/.gitignore"
run %{find . -type d -empty | grep -v "vendor" | grep -v ".git" | grep -v "tmp" | xargs -I xxx touch xxx/.gitignore}
file '.gitignore', <<-END
.DS_Store
log/*.log
tmp/**/*
config/database.yml
db/*.sqlite3
END
run "rm README"
run "rm public/index.html"
run "rm public/favicon.ico"
run "rm public/robots.txt"


generate("authenticated", "user session")
generate("roles", "Role User")

rake('db:create')
rake('db:sessions:create')
rake('acts_as_taggable:db:create')

initializer 'session_store.rb', <<-END
        ActionController::Base.session = { :session_key => '_#{(1..6).map { |x| (65 + rand(26)).chr }.join}_session', :secret => '#{(1..40).map { |x| (65 + rand(26)).chr }.join}' }
        ActionController::Base.session_store = :active_record_store
END


git :submodule => "init"

git :add => '.'
git :commit => "-a -m 'Initial commit'"

с помощью комманды

rails . -m ../template.txt

После этого можно создать обработчик запроса баннера (показ) и обработчик клика на баннер.


script/generate metal get_banner
Появившийся после этого файл app/metal/get_banner.rb выглядит так:

class GetBanner
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/get_banner/
      [200, {"Content-Type" => "text/html"}, "Hello, World!"]
    else
      [404, {"Content-Type" => "text/html"}, "Not Found"]
    end
  end
end

Теперь стоит подумать об организации базы данных для того, что-бы наше преимущество в скорости обработки запроса не съедалось базой данных.
У нас имеется набор объектов WebSite (площадок), набор рекламных проектов (Projects) и набор баннеров в рекламных проектах (Banners). При этом каждый проект имеет тэги и каждая площадка тоже имеет набор тэгов. Нам необходимо создать сущность которая будет звеном между сайтом и баннером. Для того-чтобы выбор баннеров был простым запросом к БД, например:


SELECT *, MD5(RAND()) as rand  FROM sites_banners WHERE site_id=1 ORDER BY rand LIMIT 0,1
А так-же необходимо все показы и клики записывать в другую таблицу с дальнейшим обсчетом по деньгам.

Создадим базовые модели:


script/generate resource website url:string user_id:integer enabled:boolean
script/generate resource project user_id:integer name:string enabled:boolean
script/generate resource banner user_id:integer image_file_name:string image_content_type:string image_file_size:integer project_id:integer enabled:boolean
И отредактируем файлы миграции для установления значения enabled в true. (Мы заботимся о наших пользователях ;))

После этого наши модели будут выглядеть примерно так:


class Banner < ActiveRecord::Base
  belongs_to :project
  belongs_to :user
  has_attached_file :image
  validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/gif']
end

class Project < ActiveRecord::Base
  has_many :banners
  belongs_to :user
  validates_presence_of :name
end


class Website < ActiveRecord::Base
  belongs_to :user
  validates_presence_of :url
  validates_format_of :url, :with =>  /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix
end

Так-же необходимо добавить в модель юзера строки:

has_many :websites
has_many :projects
has_many :banners

Теперь перейдем к работе со статистикой, для этого создадим модель BannerView


script/generate model banner_view website_id:integer banner_id:integer request_ip:string referrer:string
Именно в нее и будет писаться вся статистика по просмотру баннеров. В дальнейшем будет производится обработка данных этой таблицы с целью извлечения из нее необходимых данных с удалением старых. Для ускорения работы модель не имеет ORM связей.

Теперь перейдем к нашему metal раздатчику баннеров. В этой части статьи будем использовать самый примитивный выбор баннера - случайный из всех
поэтому его код будет выглядеть так:


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

class GetBanner
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/get_banner\/(\d+)/
      
      banner = Banner.find :first, :order => 'rand()'
      
      unless banner.nil?
        BannerView.create(
          :website_id => $1,
          :request_ip => env['REMOTE_ADDR'],
          :banner_id => banner.id,
          :referrer => env['HTTP_REFERER']
        )
        return [301, {"location" => banner.image.url}, []]
      end
      [404, {"Content-Type" => "text/html"}, ["Not availible"]]
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end
Эта операция делает 2 довольно быстрых SQL запроса.

В следующей статье я напишу биллинг и более "качественный" выбор баннера


Использование плагина active_merchant для работы с кредитными картами

Posted on December 07, 2008
Одним из лучших плагинов для интеграции с платежными системами является плагин Active Merchant. В нем реализовано большое количество различных гейтов в популярные зарубежные платежные системы. Одной из таких платежных систем является Authorize.net которая позволяет проводить операции с банковскими картами.
Для начала нам необходимо установить сам плагин в приложение

script/plugin install http://activemerchant.googlecode.com/svn/trunk/active_merchant
После установки можно приступать к его настройке, а именно получению тестового аккаунта на Authorize.net и настройкой поведения при различных environment в rails.
В файле environment.rb необходимо дописать следующий код:

unless RAILS_ENV == 'production'
  ActiveMerchant::Billing::Base.mode = :test
end
Который сообщит приложению что надо использовать тестовый интерфейс если приложение не в production режиме.
Для успешной транзакции необходимо проделать следующие проверки:
  • проверить верность номера карты
  • Авторизовать карту на authorize.net
  • Списать средства

Проверка верности карты


creditcard = ActiveMerchant::Billing::CreditCard.new(
      :number => params[:cc_number], 
      :month => params[:cc_month],
      :year => params[:cc_year],              
      :first_name => params[:first_name],      
      :last_name => params[:last_name],
      :type => params[:cc_type],
      :verification_value => params[:cvv]
    )
    if creditcard.valid?
       #Данные карты верны
   end
При неудачной проверке ошибки сходны с ошибками ActiveRecord

Авторизация карты на Authorize.net


 options = {
      :address => {},
      :billing_address => { 
        :name => params[:full_name],
        :address1 => params[:address],
        :city     => params[:city],
        :state    => params[:state],
        :country  => params[:country],
        :zip      => params[:zip],
        :phone    => params[:phone]
      }
    }
   
    gateway = AuthorizeNetGateway.new(
      :login => 'APILOGINLOGINLOGIN',         #API Login ID
      :password => 'APIPASSWORD' #API Password
    )

    response = gateway.authorize(1000, creditcard, options)
    unless response.success?
      @message = response.message.to_s
      render :action => 'error'
    end
Следует помнить, что суммы списываются в центах.

Списание средств


 gateway.capture(1000, response.authorization)
После чего с карты будет списано 10 долларов.

Новые книги о rails 2.1

Posted on June 10, 2008
rails 2.1
Недавно Carlos Brando Написал небольшую книгу в которой описаны нововведения в rails 2.1.
Объяснены такие вещи как sum метод в AR, "грязные" объекты, вычисления и многое другое. Cкачать английский вариант книги можно со странички автора

Разработка на mod_rails под Leopard

Posted on May 29, 2008
Являясь счастливым обладателем нового mac book я решил поставить под него все необходимые gem для разработки на rails. Одним из них является mod_rails. Для установки необходимо поставить нормальные ruby и rubygems (руководство). После чего ставим mod_rails

gem install passenger
Затем запускается конфигурация (как в статье об установке под Linux ) c отличием только в том, что необходимо вписать в конфигурационный файл apache строки вручную

LoadModule passenger_module /opt/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/ext/apache2/mod_passenger.so
   RailsSpawnServer /opt/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/bin/passenger-spawn-server
   RailsRuby /opt/local/bin/ruby

Создаем виртуальные хосты и запускаем apache.
Все готово

Социальная сеть за 5 минут

Posted on May 27, 2008
Недавно появился новый rails engine Community engine который позволяет создать каркас социальной сети с блогами, профилями, системой регистрации и авторизации, а так-же закачкой новых фотографий на базе rails приложения за 5 минут.
Для установки необходимо установить Rails Engines

svn export http://svn.rails-engines.org/engines/branches/rb_2.0/ vendor/plugins/engines
И установить сам Community Engine

git clone --depth 1 git@github.com:bborn/communityengine.git vendor/plugins/community_engine
и сгенерировать миграцию
script/generate plugin_migration
Всё, социальная сеть работает

Rails наступает на массовый рынок хостинга или mod_rails

Posted on April 14, 2008
Одной из вех существования rails как фреймворка можно назвать появление его на массовых хостингах. Обычно для существования приложения необходимо собрать довольно непростую связку из веб сервера и нескольких mongrel`ов. Но теперь появился модуль для самого распространенного веб сервера массового хостинга - Apache
Модуль называется mod_rails (passenger) . Для первой установки я выбрал виртуальную машину с установленном на ней Debian Etch
Для установки mod_rails необходимо поставить следующие пакеты:

apt-get install mysql-server ruby libmysql-ruby rdoc1.8 ri1.8 apache2-mpm-prefork ruby1.8-dev build-essential apache2-prefork-dev libapr1-dev libopenssl-ruby1.8 irb
После чего необходимо установить rubygems скачав его c RubyForge и выполнив
 ruby setp.rb 
После чего можно поставить passenger.
После установки можно приступать к конфигурации Apache, для этого надо собрать mod_rails коммандой

/usr/bin/passenger-install-apache2-module
И настроить его написав 2 файла:

userad-virual-www:~# cat /etc/apache2/mods-available/rails.load
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-1.0.1/ext/apache2/mod_passenger.so
userad-virual-www:~# cat /etc/apache2/mods-available/rails.conf
<IfModule passenger_module>
  RailsSpawnServer /usr/bin/passenger-spawn-server
  RailsRuby /usr/bin/ruby1.8
</IfModule>
после чего необходимо подключить его в конфиг Apache

a2enmod rails
a2enmod rewrite
А затем правим дефолтный сайт (или создаем свой новый) для того что-бы его DocumentRoot указывал на public приложения.
Перезапускаем apache и приложение запускается в production. Если есть необходимость изменить приложение без перезпуска apache то необходимо создать файл tmp/restart.txt в приложении.

Вызов PHP кода из rails приложения

Posted on August 26, 2007
Иногда необходимо вызвать PHP код из приложения
Не спрашивайте почему мне этого захотелос. Ответ банальный - лень :)
Итак, в lib/ проекта кидаем файл php_bridge.rb следующего содержания

module PHPBridge
  def self.send_to_php(file,req)
    require 'uri'
    req1 = []
    req.env.each do |name,value|
     req1.push("#{name}[=]#{value}")
    end
    params = URI.escape(req1.join("[|]"))
    req1 = []
    req.parameters.each do |name,value|
     req1.push("#{name}[=]#{value}")
    end
    param = URI.escape(req1.join("[|]"))
    `/usr/bin/php #{RAILS_ROOT}/wrappers/php-wrapper.php #{file} "#{params}" "#{param}"`
    end
end
А в контроллер который должен вызвать необходимо написать что-то похожее на:

class TestController < ApplicationController
    include PHPBridge
    def index
        @output = PHPBridge::send_to_php('test.php',request)
    end
end
После этого при запросе вызовется файл wrappers/php-wrapper.php в который необходимо написать следующий код:

<?
    $file = $argv[1];
    $params = $argv[2];
    $params = urldecode($params);
    $params = explode("[|]",$params);
    $params2 = array();
    foreach($params as $value)
    {
        $tt = explode('[=]',$value);
        $params2[$tt[0]] = $tt[1];
    }
    $_SERVER = $params2;

    $params = $argv[3];
    $params = urldecode($params);
    $params = explode("[|]",$params);
    $params2 = array();
    foreach($params as $value)
    {
        $tt = explode('[=]',$value);
        $params2[$tt[0]] = $tt[1];
    }

    $_GET = $params2;
    $_POST = $params2;

    require_once('php-code/' . $file);
?>
Вот и всё. Для теста я выводил переменные окружения.

Вот такое соревнование намечается

Posted on August 21, 2007

мне только не понятно почему Engines не нравятся