├── .gitignore ├── migrations └── .gitsave ├── .rspec ├── Rakefile ├── views ├── index.erb └── layout.erb ├── config.ru ├── public ├── css │ ├── application.css │ └── normalize.css └── favicon.ico ├── scripts └── create_databases.sql ├── .env.example ├── spec ├── features │ └── application_spec.rb └── spec_helper.rb ├── boot.rb ├── Gemfile ├── application.rb ├── lib └── tasks │ └── db.rb ├── LICENSE ├── README.md └── Gemfile.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /migrations/.gitsave: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require './lib/tasks/db' 2 | 3 | -------------------------------------------------------------------------------- /views/index.erb: -------------------------------------------------------------------------------- 1 |

2 | Welcome! 3 |

-------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require_relative './boot' 2 | run Application -------------------------------------------------------------------------------- /public/css/application.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: orange; 3 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikegehard/sinatra-postgres-starter-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /scripts/create_databases.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE 2 | WITH OWNER = ; 3 | 4 | CREATE DATABASE 5 | WITH OWNER = ; 6 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL_DEVELOPMENT='postgres://:@localhost/' 2 | DATABASE_URL_TEST='postgres://:@localhost/' -------------------------------------------------------------------------------- /spec/features/application_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'capybara/rspec' 3 | 4 | Capybara.app = Application 5 | 6 | feature 'Homepage' do 7 | scenario 'Shows the welcome message' do 8 | visit '/' 9 | 10 | expect(page).to have_content 'Welcome!' 11 | end 12 | end -------------------------------------------------------------------------------- /boot.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift('./') 2 | 3 | require 'sequel' 4 | require 'application' 5 | require 'dotenv' 6 | 7 | Dotenv.load 8 | 9 | environment = ENV["RACK_ENV"] || "development" 10 | 11 | connection_string = ENV["DATABASE_URL"] || ENV["DATABASE_URL_#{environment.upcase}"] 12 | 13 | DB = Sequel.connect(connection_string) -------------------------------------------------------------------------------- /views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | <%= yield %> 11 |
12 | 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'sinatra', '~> 1.4.5' 4 | gem 'sequel', '~> 4.9.0' 5 | gem 'pg', '~> 0.17.1' 6 | gem 'dotenv', '~> 0.10.0' 7 | 8 | group :test do 9 | gem 'database_cleaner', '~> 1.2.0' 10 | gem 'rspec', '~> 2.14.1' 11 | gem 'capybara', '~> 2.2.1' 12 | gem 'rerun', '~> 0.9.0' 13 | gem 'launchy', '~> 2.4.2' 14 | end 15 | -------------------------------------------------------------------------------- /application.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra/base' 2 | 3 | class Application < Sinatra::Application 4 | 5 | def initialize(app=nil) 6 | super(app) 7 | 8 | # initialize any other instance variables for you 9 | # application below this comment. One example would be repositories 10 | # to store things in a database. 11 | 12 | end 13 | 14 | get '/' do 15 | erb :index 16 | end 17 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RACK_ENV'] = 'test' 2 | 3 | require_relative '../boot' 4 | 5 | require 'lib/tasks/db' 6 | require 'database_cleaner' 7 | 8 | RSpec.configure do |config| 9 | config.order = 'random' 10 | 11 | config.before(:suite) do 12 | DatabaseCleaner.strategy = :transaction 13 | DatabaseCleaner.clean_with(:truncation) 14 | end 15 | 16 | config.before(:each) do 17 | DatabaseCleaner.start 18 | end 19 | 20 | config.after(:each) do 21 | DatabaseCleaner.clean 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /lib/tasks/db.rb: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'dotenv/tasks' 3 | 4 | namespace :db do 5 | desc 'Run migrations up to specified version or to latest.' 6 | task :migrate, [:version] => [:dotenv] do |_, args| 7 | require 'sequel' 8 | Sequel.extension :migration 9 | 10 | environment = ENV['RACK_ENV'] || 'development' 11 | version = args[:version] 12 | migrations_directory = 'migrations' 13 | connection_string = ENV['DATABASE_URL'] || ENV["DATABASE_URL_#{environment.upcase}"] 14 | 15 | raise "Missing Connection string" if connection_string.nil? 16 | 17 | db = Sequel.connect(connection_string) 18 | message = if args[:version].nil? 19 | Sequel::Migrator.run(db, migrations_directory) 20 | 'Migrated to latest' 21 | else 22 | Sequel::Migrator.run(db, migrations_directory, target: version.to_i) 23 | "Migrated to version #{version}" 24 | end 25 | 26 | puts message if environment != 'test' 27 | end 28 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mike Gehard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sinatra template application 2 | 3 | This application can be used as a starting point for a [Postgres](http://www.postgresql.org/) 4 | database backed, via [Sequel](http://sequel.jeremyevans.net/), application that uses 5 | [Sinatra](http://www.sinatrarb.com/). It can be deployed to [Heroku](https://www.heroku.com/). 6 | 7 | There are a few things you need to change for your application: 8 | 9 | 1. Change `scripts/create_databases.sql` to create both your development and test databases. 10 | 1. Copy the `.env.example` file to `.env`. 11 | 1. Change the `.env` file to include your database connection strings for both your development 12 | and test databases. This file is ignored by git (see .gitignore) to protect your secrets 13 | from the outside world. 14 | 1. Add your migrations to the `migrations` folder. Once you have one migration there, you can 15 | delete the `.gitsave` file. You should also uncomment the line in `spec/spec_helper.rb` so that 16 | your databases will be cleaned up between test runs. 17 | 18 | ## Development 19 | 1. `bundle install` 20 | 1. Create a database by running `psql -d postgres -f scripts/create_databases.sql` 21 | 1. Run the migrations in the development database using `rake db:migrate`. If you would 22 | like to migrate to a specific version you can do so using this rake task. Run `rake -T` for 23 | details. 24 | 1. Run the migrations in the testing database using `RACK_ENV=test rake db:migrate`. 25 | 1. `rerun rackup` 26 | * running rerun will reload app when file changes are detected 27 | 1. Run tests using `rspec`. The tests will clean up the database before each test run. 28 | 29 | ## Migrations on Heroku 30 | To run the migrations on heroku, run `heroku run 'rake db:migrate'`. If you 31 | do not have a Heroku configuration variable named DATABASE_URL, then you will need to create one. 32 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.3.6) 5 | capybara (2.2.1) 6 | mime-types (>= 1.16) 7 | nokogiri (>= 1.3.3) 8 | rack (>= 1.0.0) 9 | rack-test (>= 0.5.4) 10 | xpath (~> 2.0) 11 | celluloid (0.15.2) 12 | timers (~> 1.1.0) 13 | celluloid-io (0.15.0) 14 | celluloid (>= 0.15.0) 15 | nio4r (>= 0.5.0) 16 | database_cleaner (1.2.0) 17 | diff-lcs (1.2.5) 18 | dotenv (0.10.0) 19 | ffi (1.9.3) 20 | launchy (2.4.2) 21 | addressable (~> 2.3) 22 | listen (2.7.1) 23 | celluloid (>= 0.15.2) 24 | celluloid-io (>= 0.15.0) 25 | rb-fsevent (>= 0.9.3) 26 | rb-inotify (>= 0.9) 27 | mime-types (2.1) 28 | mini_portile (0.5.2) 29 | nio4r (1.0.0) 30 | nokogiri (1.6.1) 31 | mini_portile (~> 0.5.0) 32 | pg (0.17.1) 33 | rack (1.5.2) 34 | rack-protection (1.5.3) 35 | rack 36 | rack-test (0.6.2) 37 | rack (>= 1.0) 38 | rb-fsevent (0.9.4) 39 | rb-inotify (0.9.3) 40 | ffi (>= 0.5.0) 41 | rerun (0.9.0) 42 | listen (~> 2.7) 43 | rspec (2.14.1) 44 | rspec-core (~> 2.14.0) 45 | rspec-expectations (~> 2.14.0) 46 | rspec-mocks (~> 2.14.0) 47 | rspec-core (2.14.7) 48 | rspec-expectations (2.14.5) 49 | diff-lcs (>= 1.1.3, < 2.0) 50 | rspec-mocks (2.14.5) 51 | sequel (4.9.0) 52 | sinatra (1.4.5) 53 | rack (~> 1.4) 54 | rack-protection (~> 1.4) 55 | tilt (~> 1.3, >= 1.3.4) 56 | tilt (1.4.1) 57 | timers (1.1.0) 58 | xpath (2.0.0) 59 | nokogiri (~> 1.3) 60 | 61 | PLATFORMS 62 | ruby 63 | 64 | DEPENDENCIES 65 | capybara (~> 2.2.1) 66 | database_cleaner (~> 1.2.0) 67 | dotenv (~> 0.10.0) 68 | launchy (~> 2.4.2) 69 | pg (~> 0.17.1) 70 | rerun (~> 0.9.0) 71 | rspec (~> 2.14.1) 72 | sequel (~> 4.9.0) 73 | sinatra (~> 1.4.5) 74 | -------------------------------------------------------------------------------- /public/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. 29 | * Correct `block` display not defined for `main` in IE 11. 30 | */ 31 | 32 | article, 33 | aside, 34 | details, 35 | figcaption, 36 | figure, 37 | footer, 38 | header, 39 | hgroup, 40 | main, 41 | nav, 42 | section, 43 | summary { 44 | display: block; 45 | } 46 | 47 | /** 48 | * 1. Correct `inline-block` display not defined in IE 8/9. 49 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 50 | */ 51 | 52 | audio, 53 | canvas, 54 | progress, 55 | video { 56 | display: inline-block; /* 1 */ 57 | vertical-align: baseline; /* 2 */ 58 | } 59 | 60 | /** 61 | * Prevent modern browsers from displaying `audio` without controls. 62 | * Remove excess height in iOS 5 devices. 63 | */ 64 | 65 | audio:not([controls]) { 66 | display: none; 67 | height: 0; 68 | } 69 | 70 | /** 71 | * Address `[hidden]` styling not present in IE 8/9/10. 72 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 73 | */ 74 | 75 | [hidden], 76 | template { 77 | display: none; 78 | } 79 | 80 | /* Links 81 | ========================================================================== */ 82 | 83 | /** 84 | * Remove the gray background color from active links in IE 10. 85 | */ 86 | 87 | a { 88 | background: transparent; 89 | } 90 | 91 | /** 92 | * Improve readability when focused and also mouse hovered in all browsers. 93 | */ 94 | 95 | a:active, 96 | a:hover { 97 | outline: 0; 98 | } 99 | 100 | /* Text-level semantics 101 | ========================================================================== */ 102 | 103 | /** 104 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 105 | */ 106 | 107 | abbr[title] { 108 | border-bottom: 1px dotted; 109 | } 110 | 111 | /** 112 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 113 | */ 114 | 115 | b, 116 | strong { 117 | font-weight: bold; 118 | } 119 | 120 | /** 121 | * Address styling not present in Safari and Chrome. 122 | */ 123 | 124 | dfn { 125 | font-style: italic; 126 | } 127 | 128 | /** 129 | * Address variable `h1` font-size and margin within `section` and `article` 130 | * contexts in Firefox 4+, Safari, and Chrome. 131 | */ 132 | 133 | h1 { 134 | font-size: 2em; 135 | margin: 0.67em 0; 136 | } 137 | 138 | /** 139 | * Address styling not present in IE 8/9. 140 | */ 141 | 142 | mark { 143 | background: #ff0; 144 | color: #000; 145 | } 146 | 147 | /** 148 | * Address inconsistent and variable font size in all browsers. 149 | */ 150 | 151 | small { 152 | font-size: 80%; 153 | } 154 | 155 | /** 156 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 157 | */ 158 | 159 | sub, 160 | sup { 161 | font-size: 75%; 162 | line-height: 0; 163 | position: relative; 164 | vertical-align: baseline; 165 | } 166 | 167 | sup { 168 | top: -0.5em; 169 | } 170 | 171 | sub { 172 | bottom: -0.25em; 173 | } 174 | 175 | /* Embedded content 176 | ========================================================================== */ 177 | 178 | /** 179 | * Remove border when inside `a` element in IE 8/9/10. 180 | */ 181 | 182 | img { 183 | border: 0; 184 | } 185 | 186 | /** 187 | * Correct overflow not hidden in IE 9/10/11. 188 | */ 189 | 190 | svg:not(:root) { 191 | overflow: hidden; 192 | } 193 | 194 | /* Grouping content 195 | ========================================================================== */ 196 | 197 | /** 198 | * Address margin not present in IE 8/9 and Safari. 199 | */ 200 | 201 | figure { 202 | margin: 1em 40px; 203 | } 204 | 205 | /** 206 | * Address differences between Firefox and other browsers. 207 | */ 208 | 209 | hr { 210 | -moz-box-sizing: content-box; 211 | box-sizing: content-box; 212 | height: 0; 213 | } 214 | 215 | /** 216 | * Contain overflow in all browsers. 217 | */ 218 | 219 | pre { 220 | overflow: auto; 221 | } 222 | 223 | /** 224 | * Address odd `em`-unit font size rendering in all browsers. 225 | */ 226 | 227 | code, 228 | kbd, 229 | pre, 230 | samp { 231 | font-family: monospace, monospace; 232 | font-size: 1em; 233 | } 234 | 235 | /* Forms 236 | ========================================================================== */ 237 | 238 | /** 239 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 240 | * styling of `select`, unless a `border` property is set. 241 | */ 242 | 243 | /** 244 | * 1. Correct color not being inherited. 245 | * Known issue: affects color of disabled elements. 246 | * 2. Correct font properties not being inherited. 247 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 248 | */ 249 | 250 | button, 251 | input, 252 | optgroup, 253 | select, 254 | textarea { 255 | color: inherit; /* 1 */ 256 | font: inherit; /* 2 */ 257 | margin: 0; /* 3 */ 258 | } 259 | 260 | /** 261 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 262 | */ 263 | 264 | button { 265 | overflow: visible; 266 | } 267 | 268 | /** 269 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 270 | * All other form control elements do not inherit `text-transform` values. 271 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 272 | * Correct `select` style inheritance in Firefox. 273 | */ 274 | 275 | button, 276 | select { 277 | text-transform: none; 278 | } 279 | 280 | /** 281 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 282 | * and `video` controls. 283 | * 2. Correct inability to style clickable `input` types in iOS. 284 | * 3. Improve usability and consistency of cursor style between image-type 285 | * `input` and others. 286 | */ 287 | 288 | button, 289 | html input[type="button"], /* 1 */ 290 | input[type="reset"], 291 | input[type="submit"] { 292 | -webkit-appearance: button; /* 2 */ 293 | cursor: pointer; /* 3 */ 294 | } 295 | 296 | /** 297 | * Re-set default cursor for disabled elements. 298 | */ 299 | 300 | button[disabled], 301 | html input[disabled] { 302 | cursor: default; 303 | } 304 | 305 | /** 306 | * Remove inner padding and border in Firefox 4+. 307 | */ 308 | 309 | button::-moz-focus-inner, 310 | input::-moz-focus-inner { 311 | border: 0; 312 | padding: 0; 313 | } 314 | 315 | /** 316 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 317 | * the UA stylesheet. 318 | */ 319 | 320 | input { 321 | line-height: normal; 322 | } 323 | 324 | /** 325 | * It's recommended that you don't attempt to style these elements. 326 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 327 | * 328 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 329 | * 2. Remove excess padding in IE 8/9/10. 330 | */ 331 | 332 | input[type="checkbox"], 333 | input[type="radio"] { 334 | box-sizing: border-box; /* 1 */ 335 | padding: 0; /* 2 */ 336 | } 337 | 338 | /** 339 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 340 | * `font-size` values of the `input`, it causes the cursor style of the 341 | * decrement button to change from `default` to `text`. 342 | */ 343 | 344 | input[type="number"]::-webkit-inner-spin-button, 345 | input[type="number"]::-webkit-outer-spin-button { 346 | height: auto; 347 | } 348 | 349 | /** 350 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 351 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 352 | * (include `-moz` to future-proof). 353 | */ 354 | 355 | input[type="search"] { 356 | -webkit-appearance: textfield; /* 1 */ 357 | -moz-box-sizing: content-box; 358 | -webkit-box-sizing: content-box; /* 2 */ 359 | box-sizing: content-box; 360 | } 361 | 362 | /** 363 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 364 | * Safari (but not Chrome) clips the cancel button when the search input has 365 | * padding (and `textfield` appearance). 366 | */ 367 | 368 | input[type="search"]::-webkit-search-cancel-button, 369 | input[type="search"]::-webkit-search-decoration { 370 | -webkit-appearance: none; 371 | } 372 | 373 | /** 374 | * Define consistent border, margin, and padding. 375 | */ 376 | 377 | fieldset { 378 | border: 1px solid #c0c0c0; 379 | margin: 0 2px; 380 | padding: 0.35em 0.625em 0.75em; 381 | } 382 | 383 | /** 384 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 385 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 386 | */ 387 | 388 | legend { 389 | border: 0; /* 1 */ 390 | padding: 0; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove default vertical scrollbar in IE 8/9/10/11. 395 | */ 396 | 397 | textarea { 398 | overflow: auto; 399 | } 400 | 401 | /** 402 | * Don't inherit the `font-weight` (applied by a rule above). 403 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 404 | */ 405 | 406 | optgroup { 407 | font-weight: bold; 408 | } 409 | 410 | /* Tables 411 | ========================================================================== */ 412 | 413 | /** 414 | * Remove most spacing between table cells. 415 | */ 416 | 417 | table { 418 | border-collapse: collapse; 419 | border-spacing: 0; 420 | } 421 | 422 | td, 423 | th { 424 | padding: 0; 425 | } --------------------------------------------------------------------------------