ruby w/o rails (Олександр Сімонов)
TRANSCRIPT
Ruby w/o railsOleksandr Simonov
About me
• Name: Oleksandr Simonov
• Occupation: Founder and Owner of Amoniac
• Hobby: Open Source contribution
• Github: @simonoff
Why Ruby w/o Rails?
• Rails is a monolithic stone
• Rails is slow
• Rails force you to be a lazy
rails is:
• activerecord
• activemodel
• activesupport
• actionmailer
• actionpack
• actionview
• activejob
• railties
benchmarkhttp://www.madebymarket.com/blog/dev/ruby-web-
benchmark-report.html
Lazy Rails developer
Alternatives• Rack
• Sinatra
• Cuba
• Grape (API)
• Reel
• Nahami
• Sequel
• ROM
WEB ORM
Advantages
• Faster then Rails
• Less time for app loading
• New knowledge
Disadvantages• No Rails like console
• No Rails like code autoloading
• No Rails helpers
• No "Magic"
• More code
Practical section
• Download list of Finnish companies
• Insert/Update local DB
• On API call returns e-invoice address
- app - models - services - workers - config - boot.rb - initializers - 01-dotenv.rb - 02-airbrake.rb - 03-sequel.rb - 04-sidekiq.rb - db - migrations - init.rb - config.ru
Structure
require 'rubygems'require 'bundler/setup'require_relative 'init' # Application code loadingrequire 'sidekiq/web'use Rack::ContentLengthuse Rack::CommonLogger, ::NetvisorSpreadsheets.logger # use own loggeruse Rack::ShowExceptionsrun Rack::URLMap.new('/' => ::NetvisorSpreadsheets: :Server, '/sidekiq' => Sidekiq::Web)
config.ru
init.rb# encoding: UTF-8ENV['RACK_ENV'] ||= 'development' # default environmentrequire 'rubygems'
module NetvisorSpreadsheets # Singletone extend self attr_accessor :db
def root # helper for get application root path @root_path ||= ::Pathname.new(::File.join(File.dirname(__FILE__))).expand_path end
def env # helper for get environment @env ||= ENV["RACK_ENV"] || "development" end
def logger # helper for logger @logger ||= ::Logger.new(self.root.join('log', "#{self.env}.log").to_s) endend::NetvisorSpreadsheets.logger.level = if ::NetvisorSpreadsheets.env == 'production' ::Logger::INFO else ::Logger::DEBUG end# require boot filerequire self.root.join('config', 'boot.rb').to_s
$LOAD_PATH.unshift ::NetvisorSpreadsheets.root # add root path to load path
# autoload some initializers, models, workers and servicesdef load_folder(*path) ::Dir.glob(::NetvisorSpreadsheets.root.join(*path)).each { |r| require r }end
load_folder('config', 'initializers', '**/*.rb')load_folder('app', 'models', '**/*.rb')load_folder('app', 'workers', '**/*.rb')load_folder('app', 'services', '**/*.rb')
# require sinatra server coderequire 'config/server'
boot.rb
source 'https://rubygems.org'gem 'sqlite3'gem 'dotenv'gem 'sequel'gem 'sinatra'gem 'sinatra-contrib'gem 'sidekiq'gem 'airbrake'gem 'puma'gem 'sidekiq-cron', '~> 0.3', require: falsegem 'sidekiq-unique-jobs', '~> 4.0'
Gemfile
require 'rubygems'require 'bundler/setup'require 'sequel'require 'dotenv'require 'rake'
env = ENV['RACK_ENV'] || 'development'
namespace :db do desc 'Run migrations' task :migrate, [:version] do |_t, args| ::Dotenv.load(".env", ".env.#{env}") ::Sequel.extension :migration db = ::Sequel.connect(::ENV.fetch('DATABASE_URL')) if args[:version] puts "Migrating to version #{args[:version]}" ::Sequel::Migrator.run(db, 'db/migrations', target: args[:version].to_i) else puts 'Migrating to latest' ::Sequel::Migrator.run(db, 'db/migrations') end endend
Rakefile
::Sequel.migration do transaction up do create_table :companies do primary_key :id String :company_id, null: false, index: true String :name, null: false String :einvoice_address, null: false String :einvoice_operator, null: false end end
down do drop_table :companies endend
db/migrations/001_create_companies.rb
class CompanyUpdaterWorker include ::Sidekiq::Worker sidekiq_options unique: :while_executing URL = "http://verkkolasku.tieke.fi/ExporVLOsoiteToExcel.aspx?type=csv"
def perform path = ::Tempfile.new('vlo').path if http_download_uri(::URI.parse(URL), path) ::NetvisorSpreadsheets::CompaniesFillerService.new(path).import end end
def http_download_uri(uri, filename) begin ::Net::HTTP.new(uri.host, uri.port).start do |http| http.request(Net::HTTP::Get.new(uri.request_uri)) do |response| ::File.open(filename, 'wb') do |io| response.read_body { |chunk| io.write(chunk) } end end end rescue Exception => e return false end true endend
companies_updater_worker.rb
companies_filler_service.rbclass CompanyUpdaterWorker include ::Sidekiq::Worker sidekiq_options unique: :while_executing URL = "http://verkkolasku.tieke.fi/ExporVLOsoiteToExcel.aspx?type=csv"
def perform path = ::Tempfile.new('vlo').path if http_download_uri(::URI.parse(URL), path) ::NetvisorSpreadsheets::CompaniesFillerService.new(path).import end end
def http_download_uri(uri, filename) begin ::Net::HTTP.new(uri.host, uri.port).start do |http| http.request(Net::HTTP::Get.new(uri.request_uri)) do |response| ::File.open(filename, 'wb') do |io| response.read_body { |chunk| io.write(chunk) } end end end rescue Exception => e return false end true endend
require 'sinatra/base'require 'sinatra/json'module NetvisorSpreadsheets class Server < Sinatra::Base configure :production, :development do enable :logging set :json_encoder, :to_json end
get '/' do if params['company_id'] && params['company_id'].length > 0 json ::NetvisorSpreadsheets::CompanyFinderService.find(params['company_id']) else 400 end end endend
server.rb
What we get?
• Only 40Mb RAM
• 1 second app load
• Fast deployment
Questions