├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── TODO ├── application.rb ├── boot.rb ├── config.ru ├── config ├── app_config.example.yml ├── database.example.yml ├── database.yml ├── nginx.conf └── rainbows.rb ├── controllers ├── before.rb ├── helper.rb └── root.rb ├── db ├── migrate │ ├── 20121021051546_create_posts.rb │ ├── 20121021075543_create_comments.rb │ ├── 20121021093256_create_tags.rb │ └── 20121023054611_create_users.rb ├── schema.rb └── seeds.rb ├── lib └── .gitkeep ├── models ├── comment.rb ├── post.rb ├── tag.rb └── user.rb ├── servicectl ├── test.md └── test ├── post_test.rb └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.DS_Store 3 | log 4 | log/**/* 5 | tmp/**/* 6 | bin/* 7 | .bundle/ 8 | config/database.yml 9 | config/app_config.yml 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'rack' 4 | gem 'sinatra' 5 | 6 | gem 'oj' 7 | 8 | gem 'activerecord', '~> 3.2', :require => 'active_record' 9 | gem 'mysql2' 10 | gem 'dalli', :require => 'active_support/cache/dalli_store' 11 | gem 'kgio' 12 | gem "second_level_cache", :git => "git://github.com/csdn-dev/second_level_cache.git" 13 | 14 | gem 'rake' 15 | # gem 'pony' # pony must be after activerecord 16 | 17 | group :production do 18 | gem 'rainbows' 19 | end 20 | 21 | group :development do 22 | gem 'thin' 23 | gem 'pry' 24 | gem 'sinatra-contrib' 25 | end 26 | 27 | group :test do 28 | gem 'minitest', "~>2.6.0", :require => "minitest/autorun" 29 | gem 'rack-test', :require => "rack/test" 30 | gem 'factory_girl' 31 | gem 'database_cleaner' 32 | end 33 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: git://github.com/csdn-dev/second_level_cache.git 3 | revision: f6185488ebf1bf5d1e7e18e1448c61bb367f51d2 4 | specs: 5 | second_level_cache (1.6.2) 6 | activesupport (~> 3.2.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activemodel (3.2.13) 12 | activesupport (= 3.2.13) 13 | builder (~> 3.0.0) 14 | activerecord (3.2.13) 15 | activemodel (= 3.2.13) 16 | activesupport (= 3.2.13) 17 | arel (~> 3.0.2) 18 | tzinfo (~> 0.3.29) 19 | activesupport (3.2.13) 20 | i18n (= 0.6.1) 21 | multi_json (~> 1.0) 22 | arel (3.0.2) 23 | backports (3.3.1) 24 | builder (3.0.4) 25 | coderay (1.0.9) 26 | daemons (1.1.9) 27 | dalli (2.6.3) 28 | database_cleaner (1.0.1) 29 | eventmachine (1.0.3) 30 | factory_girl (4.2.0) 31 | activesupport (>= 3.0.0) 32 | i18n (0.6.1) 33 | kgio (2.8.0) 34 | method_source (0.8.1) 35 | minitest (2.6.2) 36 | multi_json (1.7.3) 37 | mysql2 (0.3.11) 38 | oj (2.0.13) 39 | pry (0.9.12.2) 40 | coderay (~> 1.0.5) 41 | method_source (~> 0.8) 42 | slop (~> 3.4) 43 | rack (1.5.2) 44 | rack-protection (1.5.0) 45 | rack 46 | rack-test (0.6.2) 47 | rack (>= 1.0) 48 | rainbows (4.5.0) 49 | kgio (~> 2.5) 50 | rack (~> 1.1) 51 | unicorn (~> 4.6, >= 4.6.2) 52 | raindrops (0.11.0) 53 | rake (10.0.4) 54 | sinatra (1.4.2) 55 | rack (~> 1.5, >= 1.5.2) 56 | rack-protection (~> 1.4) 57 | tilt (~> 1.3, >= 1.3.4) 58 | sinatra-contrib (1.4.0) 59 | backports (>= 2.0) 60 | eventmachine 61 | rack-protection 62 | rack-test 63 | sinatra (~> 1.4.2) 64 | tilt (~> 1.3) 65 | slop (3.4.5) 66 | thin (1.5.1) 67 | daemons (>= 1.0.9) 68 | eventmachine (>= 0.12.6) 69 | rack (>= 1.0.0) 70 | tilt (1.4.1) 71 | tzinfo (0.3.37) 72 | unicorn (4.6.2) 73 | kgio (~> 2.6) 74 | rack 75 | raindrops (~> 0.7) 76 | 77 | PLATFORMS 78 | ruby 79 | 80 | DEPENDENCIES 81 | activerecord (~> 3.2) 82 | dalli 83 | database_cleaner 84 | factory_girl 85 | kgio 86 | minitest (~> 2.6.0) 87 | mysql2 88 | oj 89 | pry 90 | rack 91 | rack-test 92 | rainbows 93 | rake 94 | second_level_cache! 95 | sinatra 96 | sinatra-contrib 97 | thin 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is my sinatra boilplate for web service framework. 2 | 3 | Sinatra project root directory: settings.root 4 | 5 | console: pry -r './application' 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'active_support' 2 | require 'active_record' 3 | 4 | def get_env name, default=nil 5 | ENV[name] || ENV[name.downcase] || default 6 | end 7 | 8 | namespace :db do 9 | desc "prepare environment (utility)" 10 | task :env do 11 | require 'bundler' 12 | env = get_env 'RACK_ENV', 'development' 13 | Bundler.require :default, env.to_sym 14 | unless defined?(DB_CONFIG) 15 | databases = YAML.load_file File.dirname(__FILE__) + '/config/database.yml' 16 | DB_CONFIG = databases[env] 17 | end 18 | puts "loaded config for #{env}" 19 | end 20 | 21 | desc "connect db (utility)" 22 | task connect: :env do 23 | "connecting to #{DB_CONFIG['database']}" 24 | ActiveRecord::Base.establish_connection DB_CONFIG 25 | end 26 | 27 | desc "create db for current RACK_ENV" 28 | task create: :env do 29 | puts "creating db #{DB_CONFIG['database']}" 30 | ActiveRecord::Base.establish_connection DB_CONFIG.merge('database' => nil) 31 | ActiveRecord::Base.connection.create_database DB_CONFIG['database'], charset: 'utf8' 32 | ActiveRecord::Base.establish_connection DB_CONFIG 33 | end 34 | 35 | desc 'drop db for current RACK_ENV' 36 | task drop: :env do 37 | if get_env('RACK_ENV') == 'production' 38 | puts "cannot drop production database!" 39 | else 40 | puts "dropping db #{DB_CONFIG['database']}" 41 | ActiveRecord::Base.establish_connection DB_CONFIG.merge('database' => nil) 42 | ActiveRecord::Base.connection.drop_database DB_CONFIG['database'] 43 | end 44 | end 45 | 46 | desc 'run migrations' 47 | task migrate: :connect do 48 | version = get_env 'VERSION' 49 | version = version ? version.to_i : nil 50 | ActiveRecord::Migration.verbose = true 51 | ActiveRecord::Migrator.migrate 'db/migrate', version 52 | end 53 | 54 | desc 'rollback migrations (STEP = 1 by default)' 55 | task rollback: :connect do 56 | step = get_env 'STEP' 57 | step = step ? step.to_i : 1 58 | ActiveRecord::Migrator.rollback 'db/migrate', step 59 | end 60 | 61 | desc "show current schema version" 62 | task version: :connect do 63 | puts ActiveRecord::Migrator.current_version 64 | end 65 | end 66 | 67 | namespace :metric do 68 | desc "project statistics" 69 | task 'stat' do 70 | puts "\nRuby:" 71 | stat_files Dir.glob('**/*.rb') - Dir.glob('test/**/*.rb') 72 | end 73 | end 74 | 75 | private 76 | def stat_files fs 77 | c = 0 78 | fc = 0 79 | fs.each do |f| 80 | fc += 1 81 | data = File.binread f 82 | c += data.count "\n" 83 | end 84 | puts "files: #{fc}" 85 | puts "lines: #{c}" 86 | end 87 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * test -------------------------------------------------------------------------------- /application.rb: -------------------------------------------------------------------------------- 1 | configure do 2 | # = Configuration = 3 | set :run, false 4 | set :show_exceptions, development? 5 | set :raise_errors, development? 6 | set :logging, true 7 | set :static, false # your upstream server should deal with those (nginx, Apache) 8 | end 9 | 10 | configure :production do 11 | end 12 | 13 | # initialize log 14 | require 'logger' 15 | Dir.mkdir('log') unless File.exist?('log') 16 | class ::Logger; alias_method :write, :<<; end 17 | case ENV["RACK_ENV"] 18 | when "production" 19 | logger = ::Logger.new("log/production.log") 20 | logger.level = ::Logger::WARN 21 | when "development" 22 | logger = ::Logger.new(STDOUT) 23 | logger.level = ::Logger::DEBUG 24 | else 25 | logger = ::Logger.new("/dev/null") 26 | end 27 | # use Rack::CommonLogger, logger 28 | 29 | # initialize json 30 | # require 'active_support' 31 | # ActiveSupport::JSON::Encoding.escape_html_entities_in_json = true 32 | 33 | # initialize ActiveRecord 34 | ActiveRecord::Base.establish_connection YAML::load(File.open('config/database.yml'))[ENV["RACK_ENV"]] 35 | # ActiveRecord::Base.logger = logger 36 | ActiveSupport.on_load(:active_record) do 37 | self.include_root_in_json = false 38 | self.default_timezone = :local 39 | self.time_zone_aware_attributes = false 40 | self.logger = logger 41 | # self.observers = :cacher, :garbage_collector, :forum_observer 42 | end 43 | 44 | # load project config 45 | APP_CONFIG = YAML.load_file(File.expand_path("../config", __FILE__) + '/app_config.yml')[ENV["RACK_ENV"]] 46 | 47 | # initialize memcached 48 | # require 'dalli' 49 | # require 'active_support/cache/dalli_store' 50 | Dalli.logger = logger 51 | CACHE = ActiveSupport::Cache::DalliStore.new("127.0.0.1") 52 | 53 | # initialize ActiveRecord Cache 54 | # require 'second_level_cache' 55 | SecondLevelCache.configure do |config| 56 | config.cache_store = CACHE 57 | config.logger = logger 58 | config.cache_key_prefix = 'domain' 59 | end 60 | 61 | # Set autoload directory 62 | %w{models controllers lib}.each do |dir| 63 | Dir.glob(File.expand_path("../#{dir}", __FILE__) + '/**/*.rb').each do |file| 64 | require file 65 | end 66 | end -------------------------------------------------------------------------------- /boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set rack environment 4 | ENV['RACK_ENV'] ||= "development" 5 | 6 | # Set up gems listed in the Gemfile. 7 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__) 8 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 9 | Bundler.require(:default, ENV['RACK_ENV']) 10 | 11 | require 'sinatra' 12 | require 'sinatra/reloader' if development? 13 | 14 | # Set project configuration 15 | require File.expand_path("../application", __FILE__) -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # Set application dependencies 2 | require File.expand_path("../boot", __FILE__) 3 | 4 | # release thread current connection return to connection pool in multi-thread mode 5 | use ActiveRecord::ConnectionAdapters::ConnectionManagement 6 | 7 | # Boot application 8 | run Sinatra::Application 9 | -------------------------------------------------------------------------------- /config/app_config.example.yml: -------------------------------------------------------------------------------- 1 | development: &default 2 | sinatra: 'sinatra boiler plate' 3 | 4 | test: 5 | <<: *default 6 | 7 | production: 8 | <<: *default -------------------------------------------------------------------------------- /config/database.example.yml: -------------------------------------------------------------------------------- 1 | # MySQL. Versions 4.1 and 5.0 are recommended. 2 | # 3 | # Install the MYSQL driver 4 | # gem install mysql2 5 | # 6 | # Ensure the MySQL gem is defined in your Gemfile 7 | # gem 'mysql2' 8 | # 9 | # And be sure to use new-style password hashing: 10 | # http://dev.mysql.com/doc/refman/5.0/en/old-client.htmldevelopment: 11 | development: 12 | adapter: mysql2 13 | encoding: utf8 14 | reconnect: false 15 | database: test 16 | pool: 5 17 | username: root 18 | password: fankai 19 | host: localhost 20 | 21 | # Warning: The database defined as "test" will be erased and 22 | # re-generated from your development database when you run "rake". 23 | # Do not set this db to the same as development or production. 24 | test: 25 | adapter: mysql2 26 | encoding: utf8 27 | reconnect: false 28 | database: test 29 | pool: 5 30 | username: root 31 | password: fankai 32 | host: localhost 33 | 34 | production: 35 | adapter: mysql2 36 | encoding: utf8 37 | reconnect: false 38 | database: test 39 | pool: 64 40 | username: root 41 | password: fankai 42 | host: localhost 43 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # MySQL. Versions 4.1 and 5.0 are recommended. 2 | # 3 | # Install the MYSQL driver 4 | # gem install mysql2 5 | # 6 | # Ensure the MySQL gem is defined in your Gemfile 7 | # gem 'mysql2' 8 | # 9 | # And be sure to use new-style password hashing: 10 | # http://dev.mysql.com/doc/refman/5.0/en/old-client.htmldevelopment: 11 | development: 12 | adapter: mysql2 13 | encoding: utf8 14 | reconnect: false 15 | database: test 16 | pool: 5 17 | username: root 18 | password: fankai 19 | host: localhost 20 | 21 | # Warning: The database defined as "test" will be erased and 22 | # re-generated from your development database when you run "rake". 23 | # Do not set this db to the same as development or production. 24 | test: 25 | adapter: mysql2 26 | encoding: utf8 27 | reconnect: false 28 | database: test 29 | pool: 5 30 | username: root 31 | password: fankai 32 | host: localhost 33 | 34 | production: 35 | adapter: mysql2 36 | encoding: utf8 37 | reconnect: false 38 | database: test 39 | pool: 64 40 | username: root 41 | password: fankai 42 | host: localhost 43 | -------------------------------------------------------------------------------- /config/nginx.conf: -------------------------------------------------------------------------------- 1 | http { 2 | include mime.types; 3 | default_type application/octet-stream; 4 | access_log off; 5 | server_tokens off; 6 | charset utf-8; 7 | client_max_body_size 5M; 8 | keepalive_timeout 60 20; 9 | send_timeout 10; 10 | sendfile on; 11 | tcp_nopush on; 12 | tcp_nodelay off; 13 | 14 | gzip on; 15 | gzip_min_length 1k; 16 | gzip_disable "MSIE [1-6]\."; 17 | gzip_http_version 1.1; 18 | gzip_types text/plain text/css application/x-javascript application/xml application/json application/atom+xml application/rss+xml; 19 | gzip_vary on; 20 | 21 | server { 22 | listen 80; 23 | server_name localhost; 24 | location / { 25 | root html; 26 | index index.html index.htm; 27 | } 28 | error_page 500 502 503 504 /50x.html; 29 | location = /50x.html { 30 | root html; 31 | } 32 | } 33 | 34 | server { 35 | listen 80; 36 | server_name www.robbinfan.com; 37 | root /webroot/robbin_site/public; 38 | rewrite ^/(.*)$ http://robbinfan.com/$1 permanent; 39 | } 40 | 41 | server { 42 | listen 80; 43 | server_name robbinfan.com; 44 | root /webroot/robbin_site/public; 45 | rewrite /leanstartup/lean_startup_note.html /blog/27/lean-startup permanent; 46 | add_header X-UA-Compatible IE=Edge,chrome=1; 47 | 48 | location @rainbows { 49 | proxy_set_header X-Real-IP $remote_addr; 50 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 51 | proxy_set_header Host $http_host; 52 | proxy_pass http://127.0.0.1:8080; 53 | } 54 | 55 | location / { 56 | try_files $uri @rainbows; 57 | } 58 | 59 | location ~ ^/skins/default/(.*).(png|gif)$ { 60 | access_log off; 61 | error_log /dev/null crit; 62 | expires 3d; 63 | add_header Cache-Control public; 64 | add_header ETag ""; 65 | break; 66 | } 67 | 68 | location ~ ^/(images|javascripts|skins|stylesheets|uploads)/ { 69 | access_log off; 70 | error_log /dev/null crit; 71 | expires max; 72 | add_header Cache-Control public; 73 | add_header ETag ""; 74 | break; 75 | } 76 | 77 | error_page 404 406 /404.html; 78 | error_page 500 502 503 504 /500.html; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /config/rainbows.rb: -------------------------------------------------------------------------------- 1 | # rainbows config 2 | Rainbows! do 3 | use :ThreadPool 4 | # use :ThreadSpawn 5 | worker_connections 64 6 | end 7 | 8 | # paths and things 9 | wd = File.expand_path('../../', __FILE__) 10 | tmp_path = File.join(wd, 'log') 11 | Dir.mkdir(tmp_path) unless File.exist?(tmp_path) 12 | socket_path = File.join(tmp_path, 'rainbows.sock') 13 | pid_path = File.join(tmp_path, 'rainbows.pid') 14 | err_path = File.join(tmp_path, 'rainbows.error.log') 15 | out_path = File.join(tmp_path, 'rainbows.out.log') 16 | 17 | # Use at least one worker per core if you're on a dedicated server, 18 | # more will usually help for _short_ waits on databases/caches. 19 | worker_processes 2 20 | 21 | # If running the master process as root and the workers as an unprivileged 22 | # user, do this to switch euid/egid in the workers (also chowns logs): 23 | # user "unprivileged_user", "unprivileged_group" 24 | 25 | # tell it where to be 26 | working_directory wd 27 | 28 | # listen on both a Unix domain socket and a TCP port, 29 | # we use a shorter backlog for quicker failover when busy 30 | listen 8080, :tcp_nopush => true 31 | 32 | # nuke workers after 30 seconds instead of 60 seconds (the default) 33 | timeout 30 34 | 35 | # feel free to point this anywhere accessible on the filesystem 36 | pid pid_path 37 | 38 | # By default, the Unicorn logger will write to stderr. 39 | # Additionally, ome applications/frameworks log to stderr or stdout, 40 | # so prevent them from going to /dev/null when daemonized here: 41 | stderr_path err_path 42 | stdout_path out_path 43 | 44 | preload_app true 45 | 46 | before_fork do |server, worker| 47 | # # This allows a new master process to incrementally 48 | # # phase out the old master process with SIGTTOU to avoid a 49 | # # thundering herd (especially in the "preload_app false" case) 50 | # # when doing a transparent upgrade. The last worker spawned 51 | # # will then kill off the old master process with a SIGQUIT. 52 | old_pid = "#{server.config[:pid]}.oldbin" 53 | 54 | if old_pid != server.pid 55 | begin 56 | sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU 57 | Process.kill(sig, File.read(old_pid).to_i) 58 | rescue Errno::ENOENT, Errno::ESRCH 59 | end 60 | end 61 | # 62 | # Throttle the master from forking too quickly by sleeping. Due 63 | # to the implementation of standard Unix signal handlers, this 64 | # helps (but does not completely) prevent identical, repeated signals 65 | # from being lost when the receiving process is busy. 66 | # sleep 1 67 | end 68 | 69 | after_fork do |server, worker| 70 | end -------------------------------------------------------------------------------- /controllers/before.rb: -------------------------------------------------------------------------------- 1 | before do 2 | response['X-UA-Compatible'] = "IE=edge,chrome=1" 3 | end 4 | 5 | -------------------------------------------------------------------------------- /controllers/helper.rb: -------------------------------------------------------------------------------- 1 | helpers do 2 | def bar(name) 3 | "#{name}bar" 4 | end 5 | end -------------------------------------------------------------------------------- /controllers/root.rb: -------------------------------------------------------------------------------- 1 | get '/' do 2 | p = Post.find :first 3 | "Hello world! #{p.name}\n" 4 | end 5 | 6 | get '/post/:id' do 7 | content_type :json 8 | p = Post.find params[:id] 9 | Post.find_by_sql "SELECT SLEEP(1);" 10 | p.to_json 11 | end 12 | 13 | get '/example.json' do 14 | content_type :json 15 | { :key1 => 'value1', :key2 => 'value2' }.to_json 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20121021051546_create_posts.rb: -------------------------------------------------------------------------------- 1 | class CreatePosts < ActiveRecord::Migration 2 | def change 3 | create_table :posts do |t| 4 | t.string :name 5 | t.string :title 6 | t.text :content 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121021075543_create_comments.rb: -------------------------------------------------------------------------------- 1 | class CreateComments < ActiveRecord::Migration 2 | def change 3 | create_table :comments do |t| 4 | t.string :commenter 5 | t.text :body 6 | t.references :post 7 | 8 | t.timestamps 9 | end 10 | add_index :comments, :post_id 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20121021093256_create_tags.rb: -------------------------------------------------------------------------------- 1 | class CreateTags < ActiveRecord::Migration 2 | def change 3 | create_table :tags do |t| 4 | t.string :name 5 | t.references :post 6 | 7 | t.timestamps 8 | end 9 | add_index :tags, :post_id 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121023054611_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | t.string :name, :null => false, :limit => 30 5 | t.boolean :gender 6 | t.integer :age, :null => false, :default => 0 7 | t.date :birthday, :null => true 8 | t.string :industry, :null => false, :default => "Internet" 9 | 10 | t.timestamps 11 | end 12 | 13 | add_index :users, :industry 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended to check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(:version => 20121023054611) do 15 | 16 | create_table "comments", :force => true do |t| 17 | t.string "commenter" 18 | t.text "body" 19 | t.integer "post_id" 20 | t.datetime "created_at", :null => false 21 | t.datetime "updated_at", :null => false 22 | end 23 | 24 | add_index "comments", ["post_id"], :name => "index_comments_on_post_id" 25 | 26 | create_table "posts", :force => true do |t| 27 | t.string "name" 28 | t.string "title" 29 | t.text "content" 30 | t.datetime "created_at", :null => false 31 | t.datetime "updated_at", :null => false 32 | end 33 | 34 | create_table "tags", :force => true do |t| 35 | t.string "name" 36 | t.integer "post_id" 37 | t.datetime "created_at", :null => false 38 | t.datetime "updated_at", :null => false 39 | end 40 | 41 | add_index "tags", ["post_id"], :name => "index_tags_on_post_id" 42 | 43 | create_table "users", :force => true do |t| 44 | t.string "name", :limit => 30, :null => false 45 | t.boolean "gender" 46 | t.integer "age", :default => 0, :null => false 47 | t.date "birthday" 48 | t.string "industry", :default => "Internet", :null => false 49 | t.datetime "created_at", :null => false 50 | t.datetime "updated_at", :null => false 51 | end 52 | 53 | add_index "users", ["industry"], :name => "index_users_on_industry" 54 | 55 | end 56 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robbin/sinatratest/7fd676e9e6432a8cbe8963c795969eef88db98c9/lib/.gitkeep -------------------------------------------------------------------------------- /models/comment.rb: -------------------------------------------------------------------------------- 1 | class Comment < ActiveRecord::Base 2 | attr_accessible :body, :commenter 3 | belongs_to :post 4 | end 5 | -------------------------------------------------------------------------------- /models/post.rb: -------------------------------------------------------------------------------- 1 | class Post < ActiveRecord::Base 2 | acts_as_cached 3 | attr_accessible :content, :name, :title, :tags_attributes 4 | 5 | validates :name, :presence => true 6 | validates :title, :presence => true, :length => {:minimum => 5} 7 | 8 | has_many :comments, :dependent => :destroy 9 | has_many :tags 10 | 11 | accepts_nested_attributes_for :tags, 12 | :allow_destroy => :true, 13 | :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } } 14 | 15 | end 16 | -------------------------------------------------------------------------------- /models/tag.rb: -------------------------------------------------------------------------------- 1 | class Tag < ActiveRecord::Base 2 | attr_accessible :name 3 | belongs_to :post 4 | end 5 | -------------------------------------------------------------------------------- /models/user.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding : utf-8 -*- 2 | class User < ActiveRecord::Base 3 | attr_accessible :name, :gender, :birthday, :age, :industry 4 | # validates :name, :presence => {:message => "必须填写"}, :length => {:minimum => 5, :message => "必须大于 %{count}"} 5 | validates :name, :presence => true, :length => {:minimum => 5} 6 | 7 | end 8 | -------------------------------------------------------------------------------- /servicectl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # set ruby GC parameters 4 | RUBY_HEAP_MIN_SLOTS=600000 5 | RUBY_FREE_MIN=200000 6 | RUBY_GC_MALLOC_LIMIT=60000000 7 | export RUBY_HEAP_MIN_SLOTS RUBY_FREE_MIN RUBY_GC_MALLOC_LIMIT 8 | 9 | pid="log/rainbows.pid" 10 | 11 | case "$1" in 12 | start) 13 | bundle exec rainbows -c config/rainbows.rb -E production -D 14 | ;; 15 | stop) 16 | kill -QUIT `cat $pid` 17 | ;; 18 | restart) 19 | $0 stop 20 | sleep 1 21 | $0 start 22 | ;; 23 | reload) 24 | kill -USR2 `cat $pid` 25 | ;; 26 | force-stop) 27 | kill -INT `cat $pid` 28 | ;; 29 | shutdown-workers) 30 | kill -WINCH `cat $pid` 31 | ;; 32 | increment-worker) 33 | kill -TTIN `cat $pid` 34 | ;; 35 | decrement-worker) 36 | kill -TTOU `cat $pid` 37 | ;; 38 | logrotate) 39 | kill -USR1 `cat $pid` 40 | ;; 41 | *) 42 | echo $"Usage: $0 {start|stop|restart|reload|force-stop|shutdown-workers|increment-worker|decrement-worker|logrotate|}" 43 | ;; 44 | esac -------------------------------------------------------------------------------- /test.md: -------------------------------------------------------------------------------- 1 | ## siege -c10 -r50 http://localhost:8080/post/1.json 2 | 3 | ### Fiber Pool (2process * 10 fibers) 4 | CPU 0.8-1.3% 5 | Mem 57MB, 55MB 6 | Time 77.44 76.40 72.40s 7 | 8 | ### Multi-thread (2process * 10 threads) 9 | CPU 0.8-1.3% 10 | Mem 59MB, 60MB 11 | Time 73.44 71.41 73.51s 12 | 13 | ### Multi-process (2process) 14 | CPU 0.4-0.5% 15 | Mem 49MB, 45MB 16 | Time 251.48s 17 | 18 | ## siege -c20 -r50 http://localhost:8080/post/1.json 19 | 20 | ### Fiber Pool (2process * 20 fibers) 21 | CPU 2.5-3.5% 22 | Mem 64MB, 51MB 23 | Time 74.45 76.50 73.42s 24 | 25 | ### Multi-thread (2process * 10 threads) 26 | CPU 2.0-2.5% 27 | Mem 52MB, 51MB 28 | Time 73.44 78.45 80.48s 29 | 30 | --- 31 | 32 | ## siege -c10 -r50 http://localhost:8080/post/1.json 33 | 34 | ### rainbows 16 threads 35 | CPU 3% 36 | Mem 49.8MB 37 | Time 75.55 76.43 73.50s 38 | 39 | ### puma 16 threads 40 | CPU 3% 41 | Mem 53.6MB 42 | Time 71.53 71.55 71.43s 43 | 44 | ## siege -c10 -r50 http://localhost:8080/ 45 | 46 | ### rainbows 16 threads 47 | Mem 51.2MB 48 | Time 28.36 26.22 30.27 49 | 50 | ### puma 16 threads 51 | Mem 53.6MB 52 | Time 30.47 25.28 28.55s 53 | 54 | --- 55 | 56 | ## siege -c10 -r50 http://localhost:8080/ (rainbows 2 *16 threads) 57 | 58 | ### Dalli 59 | CPU 2% 60 | Mem 49MB, 47MB 61 | Time 26.24 23.20 26.23 62 | 63 | ### redis-store 64 | CPU 2% 65 | Mem 53MB, 50MB 66 | Time 29.24 25.25 27.26 67 | -------------------------------------------------------------------------------- /test/post_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../test_helper", __FILE__) 2 | 3 | # class PostTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | # end 8 | 9 | class PostTest < Test::Unit::TestCase 10 | include Rack::Test::Methods 11 | def test_true 12 | assert true 13 | end 14 | # def app 15 | # Sinatra::Application 16 | # end 17 | # 18 | # def test_my_default 19 | # get '/' 20 | # assert_equal 'Hello World!', last_response.body 21 | # end 22 | # 23 | # def test_with_params 24 | # get '/meet', :name => 'Frank' 25 | # assert_equal 'Hello Frank!', last_response.body 26 | # end 27 | # 28 | # def test_with_rack_env 29 | # get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' 30 | # assert_equal "You're using Songbird!", last_response.body 31 | # end 32 | end -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RACK_ENV"] = "test" 2 | require File.expand_path('../../boot', __FILE__) 3 | 4 | require 'test/unit' 5 | require 'rack/test' 6 | # require 'active_support/test_case' 7 | # require 'active_record/test_case' 8 | # 9 | # class ActiveSupport::TestCase 10 | # setup do 11 | # ActiveRecord::IdentityMap.clear 12 | # end 13 | # 14 | # end 15 | --------------------------------------------------------------------------------