napa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +21 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +172 -0
  5. data/Rakefile +3 -0
  6. data/bin/napa +17 -0
  7. data/lib/generators/scaffold.rb +67 -0
  8. data/lib/generators/templates/scaffold/.env +6 -0
  9. data/lib/generators/templates/scaffold/.env.test +7 -0
  10. data/lib/generators/templates/scaffold/.gitignore.tpl +10 -0
  11. data/lib/generators/templates/scaffold/Gemfile +28 -0
  12. data/lib/generators/templates/scaffold/README.md +3 -0
  13. data/lib/generators/templates/scaffold/Rakefile +8 -0
  14. data/lib/generators/templates/scaffold/app/apis/hello_api.rb +13 -0
  15. data/lib/generators/templates/scaffold/app.rb +21 -0
  16. data/lib/generators/templates/scaffold/config/database.yml +18 -0
  17. data/lib/generators/templates/scaffold/config/initializers/active_record.rb +5 -0
  18. data/lib/generators/templates/scaffold/config/middleware/honeybadger.rb +7 -0
  19. data/lib/generators/templates/scaffold/config.ru +17 -0
  20. data/lib/generators/templates/scaffold/console +5 -0
  21. data/lib/generators/templates/scaffold/lib/password_protected_helpers.rb +17 -0
  22. data/lib/generators/templates/scaffold/spec/apis/hello_api_spec.rb +17 -0
  23. data/lib/generators/templates/scaffold/spec/factories/.gitkeep +0 -0
  24. data/lib/generators/templates/scaffold/spec/spec_helper.rb +35 -0
  25. data/lib/napa/activerecord.rb +21 -0
  26. data/lib/napa/grape_api.rb +15 -0
  27. data/lib/napa/identity.rb +38 -0
  28. data/lib/napa/logger/log_transaction.rb +17 -0
  29. data/lib/napa/logger/logger.rb +34 -0
  30. data/lib/napa/middleware/app_monitor.rb +17 -0
  31. data/lib/napa/middleware/logger.rb +69 -0
  32. data/lib/napa/version.rb +45 -0
  33. data/lib/napa.rb +37 -0
  34. data/lib/tasks/db.rake +87 -0
  35. data/lib/tasks/deploy.rake +13 -0
  36. data/lib/tasks/git.rake +50 -0
  37. data/lib/tasks/routes.rake +9 -0
  38. data/napa.gemspec +31 -0
  39. data/spec/identity_spec.rb +50 -0
  40. data/spec/logger/log_transaction_spec.rb +34 -0
  41. data/spec/spec_helper.rb +1 -0
  42. data/spec/version_spec.rb +40 -0
  43. data/tasks/version.rake +51 -0
  44. metadata +275 -0
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .ruby-version
19
+ .ruby-gemset
20
+ .rspec
21
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in napa.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Darby Frey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Napa
2
+
3
+ The Napa gem is a simple framework for building APIs with Grape. These features include:
4
+
5
+ * Generator
6
+ * Console
7
+ * Identity
8
+ * Logging
9
+ * Deployment
10
+ * Grape Specific Features (Cache management and Route inspection) i.e. `rake routes`
11
+
12
+ ## Installation
13
+
14
+ Napa is available as a gem, to install it run:
15
+
16
+ ```
17
+ gem install napa
18
+ ```
19
+
20
+ Or, if you're using Bundler, add it to your Gemfile:
21
+
22
+ ```
23
+ gem 'napa'
24
+ ```
25
+
26
+ And run:
27
+
28
+ ```
29
+ $ bundle install
30
+ ```
31
+
32
+ ## Getting Started
33
+
34
+ Napa comes with a useful generator to quickly scaffold up a new project. Simply run:
35
+
36
+ ```
37
+ $ napa new your_project_name
38
+ ```
39
+
40
+ This will generate a basic application framework for you. It includes everything you need to get started including a Hello World API.
41
+
42
+ 1) To get started, run Bundler to make sure you have all the gems for the project:
43
+
44
+ ```
45
+ $ bundle install
46
+ ```
47
+
48
+ 2) Then, make sure your database connections are setup correctly. The configuration is set in the `.env` and `.env.test`. Then create your database by running:
49
+
50
+ ```
51
+ $ rake db:create
52
+ ```
53
+
54
+ 3) Now you're ready to start up the server:
55
+
56
+ ```
57
+ $ shotgun
58
+ ```
59
+
60
+ 4) Once the server is started, run the following command to load your service in a browser:
61
+
62
+ ```
63
+ $ open http://127.0.0.1:9393/hello
64
+ ```
65
+
66
+ ...and you should see:
67
+
68
+ ```
69
+ {
70
+ message: "Hello Wonderful World!"
71
+ }
72
+ ```
73
+
74
+ 5) We've also provided a sample spec file. You can run the tests by running:
75
+
76
+ ```
77
+ RACK_ENV='test' rake db:test:prepare
78
+ rspec spec
79
+ ```
80
+
81
+ ## Usage/Features
82
+
83
+ ### Console
84
+ Similar to the Rails console, load an IRB sesson with your applications environment by running:
85
+
86
+ ```
87
+ ruby console
88
+ ```
89
+
90
+ ### Identity
91
+ The *Identity* module exists to provide and interface to get information about the application. For example:
92
+
93
+ `Napa::Identity.name` => Returns the name of the app defined by `ENV['SERVICE_NAME']`.
94
+
95
+ `Napa::Identity.hostname` => Returns the name of the host running the application.
96
+
97
+ `Napa::Identity.revision` => Returns the current revision from Git.
98
+
99
+ `Napa::Identity.pid` => Returns the current running process id.
100
+
101
+ ### Logger
102
+ The *Logger* modules is used to create a common log format across applications. The Logger is enable via a rack middleware by adding the line below to your `config.ru` file:
103
+
104
+ ```ruby
105
+ use Napa::Middleware::Logger
106
+ ```
107
+
108
+ You can also enable the logger for ActiveRecord by adding the following line to an initializer:
109
+
110
+ ```ruby
111
+ ActiveRecord::Base.logger = Napa::Logger.logger
112
+ ```
113
+
114
+ `Napa::Logger.logger` returns a Singleton instance of the Logging object, so it can be passed to other libraries or called directly. For example:
115
+
116
+ ```ruby
117
+ Napa::Logger.logger.debug 'Some Debug Message'
118
+ ```
119
+
120
+ ### Deployment
121
+ At Belly we leverage a git based deployment process, so we've included some rake tasks we use to automate deployments. These tasks will essentially just tag a commit with `production` or `staging` so that it can be picked up by a separate deployment process.
122
+
123
+ The tasks currently available are:
124
+
125
+ ```ruby
126
+ rake deploy:staging
127
+ ```
128
+
129
+
130
+ ```ruby
131
+ rake deploy:production
132
+ ```
133
+
134
+ **Please Note:** These tasks rely on two environment variables - `GITHUB_OAUTH_TOKEN` and `GITHUB_REPO`. For more information, see **Environment Variables** below.
135
+
136
+ ### Grape Specific Features
137
+ At Belly we use the [Grape Micro-Framework](https://github.com/intridea/grape) for many services, so we've included a few common features.
138
+
139
+ #### Cache Managment
140
+ Cache control headers are sent with Grape API responses to prevent clients from caching responses unexpectedly. This feature is enabled by default, so you don't have to make any changes to enable it.
141
+
142
+ #### Route Inspection
143
+ A rake task is included to give you a Rails style list of your routes. Simpley run:
144
+
145
+ ```ruby
146
+ rake routes
147
+ ```
148
+
149
+ ### Environment Variables
150
+ Napa expects to find some environment variables set in your application in order for some features to work. A list is below:
151
+
152
+ #### SERVICE_NAME
153
+ The name of your app or service, used by Identity and as a label for your logs
154
+
155
+ #### GITHUB\_OAUTH\_TOKEN
156
+ Used to grant access to your application on Github for deployment tagging
157
+
158
+ #### GITHUB_REPO
159
+ Your application's Github repo. i.e. `bellycard/napa`
160
+
161
+ ## Contributing
162
+
163
+ 1. Fork it
164
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
165
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
166
+ 4. Push to the branch (`git push origin my-new-feature`)
167
+ 5. Create new Pull Request
168
+
169
+
170
+ ## Todo/Feature Requests
171
+
172
+ * Add specs for logger and logging middleware
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env rake
2
+ Dir.glob('./tasks/*.rake').each { |r| import r }
3
+ require "bundler/gem_tasks"
data/bin/napa ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'napa'
4
+
5
+ actions = ['new']
6
+
7
+ if actions.include?(ARGV[0])
8
+ if ARGV[0] == 'new'
9
+ if ARGV[1].nil?
10
+ STDOUT.write "Missing App Name, Exiting\n"
11
+ else
12
+ Napa::Generators::Scaffold.new(ARGV[1], ARGV[2])
13
+ end
14
+ end
15
+ else
16
+ STDOUT.write "Unknown Action, Exiting.\n"
17
+ end
@@ -0,0 +1,67 @@
1
+ module Napa
2
+ module Generators
3
+ class Scaffold
4
+ def initialize(app_name, app_path=nil)
5
+ @app_name = app_name
6
+ @app_path = app_path.nil? ? app_name : app_path
7
+ @template_path = File.expand_path(File.join(File.dirname(__FILE__), 'templates/scaffold'))
8
+ @files = file_list
9
+
10
+ create_files!
11
+
12
+ STDOUT.write "Generator Finished!"
13
+ end
14
+
15
+ def file_list
16
+ files = [
17
+ "app/apis/hello_api.rb",
18
+ "app/models/.keep",
19
+ "config/database.yml",
20
+ "config/initializers/active_record.rb",
21
+ "config/middleware/honeybadger.rb",
22
+ "db/migrate/.keep",
23
+ "lib/tasks/.keep",
24
+ "log/.keep",
25
+ "public/.keep",
26
+ "spec/spec_helper.rb",
27
+ "spec/apis/hello_api_spec.rb",
28
+ "tmp/.keep",
29
+ ".env",
30
+ ".env.test",
31
+ ".gitignore",
32
+ "app.rb",
33
+ "config.ru",
34
+ "console",
35
+ "Gemfile",
36
+ "Rakefile",
37
+ "README.md"
38
+ ]
39
+ end
40
+
41
+ def create_files!
42
+ @files.each do |file|
43
+ create_file_with_template(file)
44
+ end
45
+ end
46
+
47
+ def create_file_with_template(file)
48
+ template_file = [@template_path, file].join("/")
49
+ new_file = [@app_name, file.gsub(/.tpl$/,'')].join("/")
50
+
51
+ FileUtils.mkdir_p(File.dirname(new_file))
52
+
53
+ if File.exists?(template_file)
54
+ file_content = File.read(template_file)
55
+
56
+ # replace template variables
57
+ file_content.gsub!(/{{app_name}}/, @app_name)
58
+ File.open(new_file, 'w') { |file| file.write(file_content) }
59
+ else
60
+ FileUtils.touch(new_file)
61
+ end
62
+
63
+ STDOUT.write "Creating File: #{new_file}\n"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,6 @@
1
+ SERVICE_NAME={{app_name}}
2
+
3
+ DATABASE_USER=root
4
+ DATABASE_PASSWORD=''
5
+ DATABASE_HOST=localhost
6
+ DATABASE_NAME={{app_name}}_development
@@ -0,0 +1,7 @@
1
+ SERVICE_NAME={{app_name}}
2
+ RACK_ENV=test
3
+
4
+ DATABASE_USER=root
5
+ DATABASE_PASSWORD=''
6
+ DATABASE_HOST=localhost
7
+ DATABASE_NAME={{app_name}}_test
@@ -0,0 +1,10 @@
1
+ .ruby-gemset
2
+ .ruby-version
3
+ .DS_Store
4
+ .env
5
+ .vagrant
6
+ .rspec
7
+ .env.*
8
+ log/*
9
+ tmp/*
10
+ !.keep
@@ -0,0 +1,28 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rack-cors'
4
+ gem 'mysql2'
5
+ gem 'activerecord', '3.2.13', :require => 'active_record'
6
+ gem 'honeybadger'
7
+ gem 'json'
8
+ gem 'shotgun'
9
+ gem 'napa', :git => 'https://github.com/bellycard/napa.git'
10
+ gem 'grape-pagination'
11
+ gem 'will_paginate', '~> 3.0'
12
+
13
+ group :development,:test do
14
+ gem 'pry'
15
+ end
16
+
17
+ group :development do
18
+ gem 'rubocop'
19
+ end
20
+
21
+ group :test do
22
+ gem 'factory_girl'
23
+ gem 'rspec'
24
+ gem 'rack-test'
25
+ gem 'simplecov'
26
+ gem 'webmock'
27
+ gem 'database_cleaner'
28
+ end
@@ -0,0 +1,3 @@
1
+ # Welcome to Napa
2
+
3
+ TODO: Add an awesome README that explains how all this stuff works!
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require 'dotenv'
3
+ Dotenv.load(ENV['RACK_ENV'] == 'test' ? ".env.test" : ".env")
4
+
5
+ require './app'
6
+ require 'json'
7
+
8
+ Dir.glob('./lib/tasks/*.rake').each { |r| import r }
@@ -0,0 +1,13 @@
1
+ module HelloService
2
+ class API < Grape::API
3
+ format :json
4
+
5
+ resource :hello do
6
+ desc "Return a Hello World message"
7
+ get do
8
+ {message: "Hello Wonderful World!"}
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # load bundler
2
+ Bundler.setup(:default)
3
+ require 'napa'
4
+ Bundler.require(:default, Napa.env.to_sym)
5
+ require 'will_paginate'
6
+ require 'will_paginate/active_record'
7
+
8
+ # load environment
9
+ Dotenv.load(Napa.env.test? ? ".env.test" : ".env")
10
+
11
+ # autoload lib
12
+ Dir['./lib/**/**/*.rb'].map {|file| require file }
13
+
14
+ # autoload initalizers
15
+ Dir['./config/initializers/**/*.rb'].map {|file| require file }
16
+
17
+ # load middleware configs
18
+ Dir['./config/middleware/**/*.rb'].map {|file| require file }
19
+
20
+ # autoload app
21
+ Dir['./app/**/**/*.rb'].map {|file| require file }
@@ -0,0 +1,18 @@
1
+ defaults: &defaults
2
+ adapter: mysql2
3
+ host: <%= ENV['DATABASE_HOST'] %>
4
+ username: <%= ENV['DATABASE_USER'] %>
5
+ password: <%= ENV['DATABASE_PASSWORD'] %>
6
+ database: <%= ENV['DATABASE_NAME'] %>
7
+
8
+ production:
9
+ <<: *defaults
10
+
11
+ development:
12
+ <<: *defaults
13
+
14
+ test:
15
+ <<: *defaults
16
+
17
+ staging:
18
+ <<: *defaults
@@ -0,0 +1,5 @@
1
+ require 'erb'
2
+ db = YAML.load(ERB.new(File.read('./config/database.yml')).result)[Napa.env]
3
+ ActiveRecord::Base.establish_connection(db)
4
+ ActiveRecord::Base.logger = Napa::Logger.logger if Napa.env.development?
5
+ ActiveRecord::Base.include_root_in_json = false
@@ -0,0 +1,7 @@
1
+ require 'honeybadger/rake_handler'
2
+
3
+ Honeybadger.configure do |config|
4
+ config.rescue_rake_exceptions = true
5
+ config.environment_name = Napa.env
6
+ config.api_key = ENV['HONEYBADGER_API_KEY']
7
+ end
@@ -0,0 +1,17 @@
1
+ require './app'
2
+
3
+ # use Rack::Cors do
4
+ # allow do
5
+ # origins '*'
6
+ # resource '*', headers: :any, methods: :any
7
+ # end
8
+ # end
9
+ #
10
+ # use Honeybadger::Rack
11
+ # use Napa::Middleware::Logger
12
+
13
+ use Napa::Middleware::AppMonitor
14
+ use ActiveRecord::ConnectionAdapters::ConnectionManagement
15
+
16
+ run HelloService::API # <-- boot your service here --
17
+
@@ -0,0 +1,5 @@
1
+ require 'bundler'
2
+ require './app'
3
+ require 'irb'
4
+ require 'irb/completion'
5
+ IRB.start
@@ -0,0 +1,17 @@
1
+ module PasswordProtectedHelpers
2
+ if ENV['HEADER_PASSWORDS']
3
+ PW_ARRAY = ENV['HEADER_PASSWORDS'].split(',').collect{|pw| pw.strip}.freeze
4
+ else
5
+ PW_ARRAY = [nil].freeze
6
+ end
7
+
8
+ def authenticate headers
9
+ error!({:error => {
10
+ :code => 'bad_password',
11
+ :message => 'bad password'}}
12
+ ) unless PW_ARRAY.include? headers['Password']
13
+ end
14
+
15
+ # extend all endpoints to include this
16
+ Grape::Endpoint.send :include, self
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ def app
4
+ HelloService::API
5
+ end
6
+
7
+ describe HelloService::API do
8
+ include Rack::Test::Methods
9
+
10
+ describe 'GET /hello' do
11
+ it 'returns a hello world message' do
12
+ get '/hello'
13
+ expect(last_response.body).to eq({message: "Hello Wonderful World!"}.to_json)
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,35 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+
3
+ require 'webmock/rspec'
4
+ require "rack/test"
5
+ require 'simplecov'
6
+ require 'factory_girl'
7
+
8
+ FactoryGirl.definition_file_paths = %w{./spec/factories}
9
+ FactoryGirl.find_definitions
10
+ SimpleCov.start
11
+
12
+ require './app'
13
+ require 'database_cleaner'
14
+
15
+ # Requires supporting ruby files with custom matchers and macros, etc,
16
+ # in spec/support/ and its subdirectories.
17
+ Dir["./spec/support/**/*.rb"].each {|f| require f}
18
+
19
+
20
+ RSpec.configure do |config|
21
+ config.include FactoryGirl::Syntax::Methods
22
+
23
+ config.before(:suite) do
24
+ DatabaseCleaner.strategy = :transaction
25
+ DatabaseCleaner.clean_with(:truncation)
26
+ end
27
+
28
+ config.before(:each) do
29
+ DatabaseCleaner.start
30
+ end
31
+
32
+ config.after(:each) do
33
+ DatabaseCleaner.clean
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module Napa
2
+ class ActiveRecord
3
+ class << self
4
+ def migration_template(migration_class)
5
+ <<-MIGRATION
6
+ class #{migration_class} < ActiveRecord::Migration
7
+ def up
8
+ # create_table :foo do |t|
9
+ # t.string :name, :null => false
10
+ # end
11
+ end
12
+
13
+ def down
14
+ # drop_table :foo
15
+ end
16
+ end
17
+ MIGRATION
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ require 'grape'
2
+ require 'grape-swagger'
3
+
4
+ class Napa::GrapeAPI < Grape::API
5
+
6
+ def after
7
+ @app_response[1]["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
8
+ @app_response[1]["Pragma"] = "no-cache"
9
+ @app_response[1]["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
10
+ @app_response
11
+ end
12
+
13
+ add_swagger_documentation
14
+
15
+ end
@@ -0,0 +1,38 @@
1
+ module Napa
2
+ class Identity
3
+ def self.health
4
+ {
5
+ name: name,
6
+ hostname: hostname,
7
+ revision: revision,
8
+ pid: pid,
9
+ parent_pid: parent_pid,
10
+ platform_revision: platform_revision
11
+ }
12
+ end
13
+
14
+ def self.name
15
+ ENV['SERVICE_NAME'] || 'api-service'
16
+ end
17
+
18
+ def self.hostname
19
+ @hostname ||= `hostname`.strip
20
+ end
21
+
22
+ def self.revision
23
+ @revision ||= `git rev-parse HEAD`.strip
24
+ end
25
+
26
+ def self.pid
27
+ @pid ||= Process.pid
28
+ end
29
+
30
+ def self.parent_pid
31
+ @ppid ||= Process.ppid
32
+ end
33
+
34
+ def self.platform_revision
35
+ Napa::VERSION
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Napa
2
+ class LogTransaction
3
+ class << self
4
+ def id
5
+ Thread.current[:napa_tid].nil? ? Thread.current[:napa_tid] = SecureRandom.hex(10) : Thread.current[:napa_tid]
6
+ end
7
+
8
+ def id=(id)
9
+ Thread.current[:napa_tid] = id
10
+ end
11
+
12
+ def clear
13
+ Thread.current[:napa_tid] = nil
14
+ end
15
+ end
16
+ end
17
+ end