napa 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/LICENSE +4 -2
  4. data/README.md +25 -2
  5. data/docs/quickstart.md +27 -26
  6. data/lib/napa.rb +6 -2
  7. data/lib/napa/active_record_extensions/notifications_subscriber.rb +17 -0
  8. data/lib/napa/active_record_extensions/stats.rb +1 -14
  9. data/lib/napa/cli.rb +5 -2
  10. data/lib/napa/generators/api_generator.rb +5 -1
  11. data/lib/napa/generators/migration_generator.rb +6 -2
  12. data/lib/napa/generators/scaffold_generator.rb +4 -3
  13. data/lib/napa/generators/templates/api/app/apis/%name_tableize%_api.rb.tt +4 -5
  14. data/lib/napa/generators/templates/api/app/models/%name_underscore%.rb.tt +0 -1
  15. data/lib/napa/generators/templates/api/spec/apis/%name_tableize%_api_spec.rb.tt +16 -0
  16. data/lib/napa/generators/templates/api/spec/models/%name_underscore%_spec.rb.tt +9 -0
  17. data/lib/napa/generators/templates/migration/%migration_filename%.rb.tt +1 -1
  18. data/lib/napa/generators/templates/scaffold/.gitignore.tt +2 -0
  19. data/lib/napa/generators/templates/scaffold/Gemfile.tt +2 -2
  20. data/lib/napa/generators/templates/scaffold/app.rb +1 -1
  21. data/lib/napa/generators/templates/scaffold/config.ru.tt +1 -1
  22. data/lib/napa/generators/templates/scaffold/config/database.yml.tt +1 -0
  23. data/lib/napa/generators/templates/scaffold/spec/apis/hello_api_spec.rb.tt +1 -1
  24. data/lib/napa/generators/templates/scaffold/spec/spec_helper.rb +15 -2
  25. data/lib/napa/grape_extenders.rb +6 -2
  26. data/lib/napa/grape_extensions/error_formatter.rb +1 -1
  27. data/lib/napa/grape_extensions/grape_helpers.rb +8 -1
  28. data/lib/napa/logger/logger.rb +10 -0
  29. data/lib/napa/logger/parseable.rb +37 -0
  30. data/lib/napa/middleware/app_monitor.rb +1 -1
  31. data/lib/napa/middleware/database_stats.rb +15 -0
  32. data/lib/napa/middleware/logger.rb +6 -11
  33. data/lib/napa/middleware/request_stats.rb +7 -5
  34. data/lib/napa/{grape_extensions → output_formatters}/entity.rb +0 -0
  35. data/lib/napa/output_formatters/include_nil.rb +16 -0
  36. data/lib/napa/{grape_extensions → output_formatters}/representer.rb +2 -2
  37. data/lib/napa/rspec_extensions/response_helpers.rb +17 -0
  38. data/lib/napa/stats.rb +21 -1
  39. data/lib/napa/version.rb +1 -1
  40. data/lib/tasks/db.rake +7 -0
  41. data/lib/tasks/git.rake +3 -0
  42. data/napa.gemspec +0 -1
  43. data/spec/generators/api_generator_spec.rb +63 -0
  44. data/spec/generators/migration_generator_spec.rb +27 -0
  45. data/spec/generators/scaffold_generator_spec.rb +90 -0
  46. data/spec/grape_extensions/error_formatter_spec.rb +8 -0
  47. data/spec/grape_extensions/include_nil_spec.rb +23 -0
  48. data/spec/logger/logger_spec.rb +14 -0
  49. data/spec/logger/parseable_spec.rb +16 -0
  50. data/spec/middleware/database_stats_spec.rb +64 -0
  51. data/spec/middleware/request_stats_spec.rb +4 -5
  52. data/spec/spec_helper.rb +26 -0
  53. data/spec/stats_spec.rb +25 -1
  54. metadata +25 -20
  55. data/spec/active_record_extensions/stats_spec.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2718b1367fa6291f248a6355cbd7805307d605ef
4
- data.tar.gz: 66b0551ef54b97b7d73d5bcbd1521254f35df2b9
3
+ metadata.gz: 0eeeb8e92f9f844abe34b2a0545655227c5d493f
4
+ data.tar.gz: 2d38e5b45cab67654e24388746a3c46eb168739c
5
5
  SHA512:
6
- metadata.gz: 8270a158da3bcc9fb032ff5f32efaef70c470d43580cdd11e37e77c812e2e29ee55a3ff4300c2652df3e4b96d0cd2c6f7763e43684f9d528546555f0b519ab3c
7
- data.tar.gz: 33596208d45629e9fc4498c28cb3d5d25a2802a26ebec8ca1c19a4e44c10639ede89909e4298c7efa787fc927c9aaeed88415b048c2e916fe1ec45cb6b66bac0
6
+ metadata.gz: ac876295c7e2f01d861766a12efe01302ae3b0bf816773cc34e2a99a41fd6698821b5fc86db54f1247383cae5e3656ffe56a472f8c44bae7c3c56ccf385a4ff0
7
+ data.tar.gz: 9bb036efa3d541e90c8aa20a3b6f088daa6d4f55d17d7acb22e7ffa4786b432aba6513974a2d0459081058b86fc5e70662ba3462a01947634dddfc53453fccb1
@@ -1,6 +1,26 @@
1
1
  master
2
2
  ===
3
3
 
4
+ 0.3.0
5
+ ===
6
+ * Added `rake db:rollback` to rollback migrations just like Rails
7
+ * Fixed bug in migration generator causing constant not defined errors
8
+ * Fixed CORS config in scaffold generator
9
+ * Fixed logging bug in grape_extenders
10
+ * Set UTF-8 encoding in generated database.yml
11
+ * Removed unneeded gem dependencies (shotgun and unicorn)
12
+ * Fixed spec_helper that gets generated to ignore spec files and gems (on CI servers)
13
+ * Added spec response helpers `parsed_response`, `response_code` and `response_body` to make tests easier to DRY up
14
+ * Removed #filter and `include Napa::FilterByHash` from generated code.
15
+ * Fix when using IRB and napa console
16
+ * Added IncludeNil module for Representable/Roar output
17
+ * Template updates to include spec files for APIs
18
+ * Removing FilterByHash in the API template
19
+ * Fix when ErrorFormatter is passed a non-hash
20
+ * Added more descriptive messages on git based deploy errors
21
+ * Added RequestStats and DatabaseStats middlewares to report data to StatsD
22
+ * All String logs are now wrapped in a hash before being written to the log file
23
+
4
24
  0.2.1
5
25
  ===
6
26
  * Updated Napa console. It now takes an optional environment parameter, i.e. `napa console production`.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Darby Frey
1
+ Copyright (c) 2013 Bellycard Inc.
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,6 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
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.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ “Napa”, and the Napa logo are registered trademarks of Bellycard Inc. All rights reserved.
data/README.md CHANGED
@@ -36,7 +36,7 @@ Run `napa` terminal prompt to see available features:
36
36
  ```
37
37
  Commands:
38
38
  napa console # Start the Napa console
39
- napa generate api <api_name> # Create a Grape API, Model and Representer
39
+ napa generate api <api_name> # Create a Grape API, Model and Representer, api_name should be singular i.e. user
40
40
  napa generate migration <migration_name> # Create a Database Migration
41
41
  napa help [COMMAND] # Describe available commands or one specific command
42
42
  napa new <app_name> [app_path] # Create a scaffold for a new Napa service
@@ -94,7 +94,7 @@ The Health Check middleware will add an endpoint at `/health` that will return s
94
94
  ```
95
95
 
96
96
  ### Logger
97
- 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:
97
+ The *Logger* module 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:
98
98
 
99
99
  ```ruby
100
100
  use Napa::Middleware::Logger
@@ -111,6 +111,29 @@ ActiveRecord::Base.logger = Napa::Logger.logger
111
111
  ```ruby
112
112
  Napa::Logger.logger.debug 'Some Debug Message'
113
113
  ```
114
+ ### StatsD
115
+ There are two middlewares available to enable StatsD reporting, `RequestStats` and `DatabaseStats`. They can be enabled independently in your `config.ru` file:
116
+
117
+ ```
118
+ use Napa::Middleware::RequestStats
119
+ use Napa::Middleware::DatabaseStats
120
+ ```
121
+
122
+ **RequestStats** will emit information about your application's request count and response time.
123
+
124
+ **DatabaseStats** will emit information from ActiveRecord about query times.
125
+
126
+ ##### Configuration
127
+
128
+ To configure StatsD in your application you will need to supply the `STATSD_HOST` and `STATSD_PORT` in your environment. Optionally, if your StatsD host requires an api token (i.e. hostedgraphite), you can configure that with the `STATSD_API_KEY` environment variable.
129
+
130
+ ##### Logging
131
+
132
+ If you want to see the StatsD reporting in action you can hook up the logger to the Napa logger to see the requests in your logs.
133
+
134
+ ```
135
+ Statsd.logger = Napa::Logger.logger
136
+ ```
114
137
 
115
138
  ## Bugs & Feature Requests
116
139
  Please add an issue in [Github](https://github.com/bellycard/napa/issues) if you discover a bug or have a feature request.
@@ -13,7 +13,7 @@ gem install napa
13
13
  In this example we will create a new API to manage a directory of people. Each person will have a `name`, `job_title` and `email_address`. To get started, create a new scaffold by running:
14
14
 
15
15
  ```
16
- napa new people_service
16
+ napa new people-service
17
17
  ```
18
18
 
19
19
  **Note:** by default, Napa will configure itself to use Mysql. If you prefer to use Postgres, simply pass in the `-d=pg` option to the `napa new` command.
@@ -22,33 +22,33 @@ You will see the following output:
22
22
 
23
23
  ```
24
24
  Generating scaffold...
25
- create people_service
26
- create people_service/.env.test
27
- create people_service/.env
28
- create people_service/.gitignore
29
- create people_service/.rubocop.yml
30
- create people_service/.ruby-gemset
31
- create people_service/.ruby-version
32
- create people_service/Gemfile
33
- create people_service/README.md
34
- create people_service/Rakefile
35
- create people_service/app.rb
36
- create people_service/app/apis/application_api.rb
37
- create people_service/app/apis/hello_api.rb
38
- create people_service/config.ru
39
- create people_service/config/database.yml
40
- create people_service/config/initializers/active_record.rb
41
- create people_service/config/middleware/honeybadger.rb
42
- create people_service/db/schema.rb
43
- create people_service/lib/.keep
44
- create people_service/log/.gitkeep
45
- create people_service/spec/apis/hello_api_spec.rb
46
- create people_service/spec/factories/.gitkeep
47
- create people_service/spec/spec_helper.rb
25
+ create people-service
26
+ create people-service/.env.test
27
+ create people-service/.env
28
+ create people-service/.gitignore
29
+ create people-service/.rubocop.yml
30
+ create people-service/.ruby-gemset
31
+ create people-service/.ruby-version
32
+ create people-service/Gemfile
33
+ create people-service/README.md
34
+ create people-service/Rakefile
35
+ create people-service/app.rb
36
+ create people-service/app/apis/application_api.rb
37
+ create people-service/app/apis/hello_api.rb
38
+ create people-service/config.ru
39
+ create people-service/config/database.yml
40
+ create people-service/config/initializers/active_record.rb
41
+ create people-service/config/middleware/honeybadger.rb
42
+ create people-service/db/schema.rb
43
+ create people-service/lib/.keep
44
+ create people-service/log/.gitkeep
45
+ create people-service/spec/apis/hello_api_spec.rb
46
+ create people-service/spec/factories/.gitkeep
47
+ create people-service/spec/spec_helper.rb
48
48
  Done!
49
49
  ```
50
50
 
51
- Now, change into the `people_service` directory and run Bundler to get all the gems you need:
51
+ Now, change into the `people-service` directory and run Bundler to get all the gems you need:
52
52
 
53
53
  ```
54
54
  bundle install
@@ -238,6 +238,7 @@ All response from Napa include the `data` key. In this case, we see an array con
238
238
  {
239
239
  "data": [
240
240
  {
241
+ "object_type": "person",
241
242
  "id": 1,
242
243
  "name": "Darby Frey",
243
244
  "job_title": "Software Engineer",
@@ -287,6 +288,6 @@ So, there you have it, a new API service in minutes. It's very basic, but you ca
287
288
 
288
289
  ## Resources
289
290
 
290
- * [Code Example: people-service](https://github.com/darbyfrey/people_service)
291
+ * [Code Example: people_service](https://github.com/darbyfrey/people_service)
291
292
  * [Grape](http://intridea.github.io/grape/)
292
293
  * [Roar](https://github.com/apotonick/roar)
@@ -12,19 +12,23 @@ require 'napa/setup'
12
12
  require 'napa/version'
13
13
  require 'napa/logger/logger'
14
14
  require 'napa/logger/log_transaction'
15
+ require 'napa/logger/parseable'
15
16
  require 'napa/identity'
16
17
  require 'napa/json_error'
17
18
  require 'napa/stats'
18
19
  require 'napa/active_record_extensions/filter_by_hash'
20
+ require 'napa/active_record_extensions/stats'
19
21
  require 'napa/grape_extensions/error_formatter'
20
22
  require 'napa/grape_extensions/grape_helpers'
21
- require 'napa/grape_extensions/entity'
22
- require 'napa/grape_extensions/representer'
23
+ require 'napa/output_formatters/entity'
24
+ require 'napa/output_formatters/include_nil'
25
+ require 'napa/output_formatters/representer'
23
26
  require 'napa/grape_extenders'
24
27
  require 'napa/middleware/logger'
25
28
  require 'napa/middleware/app_monitor'
26
29
  require 'napa/middleware/authentication'
27
30
  require 'napa/middleware/request_stats'
31
+ require 'napa/middleware/database_stats'
28
32
  require 'napa/authentication'
29
33
 
30
34
  # load rake tasks if Rake installed
@@ -0,0 +1,17 @@
1
+ if defined?(ActiveRecord)
2
+ ActiveSupport::Notifications.subscribe 'sql.active_record' do |name, start, finish, id, payload|
3
+ if payload[:sql].downcase.match(/(select|update|insert|delete)(.+)/i)
4
+ table, action = Napa::ActiveRecordStats.extract_sql_content(payload[:sql])
5
+ end
6
+
7
+ if table
8
+ Napa::Stats.emitter.timing(
9
+ "sql.query_time",
10
+ (finish - start) * 1000)
11
+
12
+ Napa::Stats.emitter.timing(
13
+ "sql.table.#{table}.#{action.downcase}.query_time",
14
+ (finish - start) * 1000)
15
+ end
16
+ end
17
+ end
@@ -1,4 +1,4 @@
1
- if defined?(ActiveSupport)
1
+ if defined?(ActiveSupport) && defined?(ActiveRecord)
2
2
  module Napa
3
3
  module ActiveRecordStats
4
4
  SQL_INSERT_DELETE_PARSER_REGEXP = /^(?<action>\w+)\s(\w+)\s\W*(?<table>\w+)/
@@ -32,19 +32,6 @@ if defined?(ActiveSupport)
32
32
  end
33
33
  [table, action]
34
34
  end
35
-
36
- ActiveSupport::Notifications.subscribe 'sql.active_record' do |name, start, finish, id, payload|
37
- if payload[:sql].match(/(select|update|insert|delete)(.+)/i)
38
- table, action = extract_sql_content(payload[:sql])
39
- end
40
-
41
- if table
42
- stat_context = "#{Thread.current[:stats_context] || Napa::Identity.name + '.unknown'}"
43
- Napa::Stats.emitter.timing(
44
- "#{stat_context}.sql.#{table}.#{action.downcase}.query_time",
45
- (finish - start) * 1000)
46
- end
47
- end
48
35
  end
49
36
  end
50
37
  end
@@ -30,8 +30,8 @@ module Napa
30
30
 
31
31
  desc 'console [environment]', 'Start the Napa console'
32
32
  options aliases: 'c'
33
- def console(environment = 'development' )
34
- ENV['RACK_ENV'] = environment
33
+ def console(environment = nil)
34
+ ENV['RACK_ENV'] = environment || 'development'
35
35
 
36
36
  require 'racksh/init'
37
37
 
@@ -42,6 +42,9 @@ module Napa
42
42
  require "irb"
43
43
  require "irb/completion"
44
44
  interpreter = IRB
45
+ # IRB uses ARGV and does not expect these arguments.
46
+ ARGV.delete('console')
47
+ ARGV.delete(environment) if environment
45
48
  end
46
49
 
47
50
  Rack::Shell.init
@@ -15,10 +15,14 @@ module Napa
15
15
  name.tableize
16
16
  end
17
17
 
18
+ def output_directory
19
+ '.'
20
+ end
21
+
18
22
  def api
19
23
  self.class.source_root "#{File.dirname(__FILE__)}/templates/api"
20
24
  say 'Generating api...'
21
- directory '.', '.'
25
+ directory '.', output_directory
22
26
  say 'Done!', :green
23
27
  end
24
28
  end
@@ -15,12 +15,16 @@ module Napa
15
15
  "#{version}_#{migration_name.underscore}"
16
16
  end
17
17
 
18
+ def output_directory
19
+ './db/migrate'
20
+ end
21
+
18
22
  def migration
19
23
  self.class.source_root "#{File.dirname(__FILE__)}/templates/migration"
20
24
  say 'Generating migration...'
21
- directory '.', './db/migrate'
25
+ directory '.', output_directory
22
26
  say 'Done!', :green
23
27
  end
24
28
  end
25
29
  end
26
- end
30
+ end
@@ -15,9 +15,10 @@ module Napa
15
15
  def generate
16
16
  say 'Generating scaffold...'
17
17
 
18
- @database_gem = ['pg','postgres'].include?(options[:database]) ? 'pg' : 'mysql2'
19
- @database_adapter = ['pg','postgres'].include?(options[:database]) ? 'postgresql' : 'mysql2'
20
- @database_user = ['pg','postgres'].include?(options[:database]) ? '' : 'root'
18
+ @database_gem = ['pg','postgres'].include?(options[:database]) ? 'pg' : 'mysql2'
19
+ @database_adapter = ['pg','postgres'].include?(options[:database]) ? 'postgresql' : 'mysql2'
20
+ @database_encoding = ['pg','postgres'].include?(options[:database]) ? 'unicode' : 'utf8'
21
+ @database_user = ['pg','postgres'].include?(options[:database]) ? '' : 'root'
21
22
 
22
23
  directory ".", (app_path || app_name)
23
24
 
@@ -1,10 +1,10 @@
1
1
  class <%= name.classify.pluralize %>Api < Grape::API
2
2
  desc 'Get a list of <%= name.underscore.tableize %>'
3
3
  params do
4
- optional :ids, type: String, desc: 'comma separated <%= name.underscore %> ids'
4
+ optional :ids, type: Array, desc: 'Array of <%= name.underscore %> ids'
5
5
  end
6
6
  get do
7
- <%= name.underscore.tableize %> = <%= name.classify %>.filter(declared(params, include_missing: false))
7
+ <%= name.underscore.tableize %> = params[:ids] ? <%= name.classify %>.where(id: params[:ids]) : <%= name.classify %>.all
8
8
  represent <%= name.underscore.tableize %>, with: <%= name.classify %>Representer
9
9
  end
10
10
 
@@ -13,8 +13,7 @@ class <%= name.classify.pluralize %>Api < Grape::API
13
13
  end
14
14
 
15
15
  post do
16
- <%= name.underscore %> = <%= name.classify %>.create(declared(params, include_missing: false))
17
- error!(present_error(:record_invalid, <%= name.underscore %>.errors.full_messages)) unless <%= name.underscore %>.errors.empty?
16
+ <%= name.underscore %> = <%= name.classify %>.create!(permitted_params)
18
17
  represent <%= name.underscore %>, with: <%= name.classify %>Representer
19
18
  end
20
19
 
@@ -34,7 +33,7 @@ class <%= name.classify.pluralize %>Api < Grape::API
34
33
  put do
35
34
  # fetch <%= name.underscore %> record and update attributes. exceptions caught in app.rb
36
35
  <%= name.underscore %> = <%= name.classify %>.find(params[:id])
37
- <%= name.underscore %>.update_attributes!(declared(params, include_missing: false))
36
+ <%= name.underscore %>.update_attributes!(permitted_params)
38
37
  represent <%= name.underscore %>, with: <%= name.classify %>Representer
39
38
  end
40
39
  end
@@ -1,3 +1,2 @@
1
1
  class <%= name.classify %> < ActiveRecord::Base
2
- include Napa::FilterByHash
3
2
  end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ def app
4
+ ApplicationApi
5
+ end
6
+
7
+ describe <%= name.classify.pluralize %>Api do
8
+ include Rack::Test::Methods
9
+
10
+ describe 'e.g. GET, POST, PUT, etc.' do
11
+ it 'needs tests to be written!' do
12
+ pending('write tests for <%= name.classify.pluralize %>Api!')
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe <%= name.classify %> do
4
+
5
+ it 'needs tests to be written!' do
6
+ pending('write tests for <%= name.classify %>!')
7
+ end
8
+
9
+ end
@@ -1,4 +1,4 @@
1
- class <%= migration_name.classify %> < ActiveRecord::Migration
1
+ class <%= migration_name.camelize %> < ActiveRecord::Migration
2
2
  def change
3
3
  end
4
4
  end
@@ -8,3 +8,5 @@ log/*
8
8
  tmp/*
9
9
  coverage
10
10
  !.keep
11
+ .ruby-gemset
12
+ .ruby-version
@@ -6,7 +6,6 @@ gem '<%= @database_gem %>'
6
6
  gem 'activerecord', '~> 4.0.0', :require => 'active_record'
7
7
  gem 'honeybadger'
8
8
  gem 'json'
9
- gem 'shotgun'
10
9
  gem 'napa'
11
10
  gem 'roar'
12
11
  gem 'grape-swagger'
@@ -16,7 +15,8 @@ group :development,:test do
16
15
  end
17
16
 
18
17
  group :development do
19
- gem 'rubocop'
18
+ gem 'rubocop', require: false
19
+ gem 'shotgun', require: false
20
20
  end
21
21
 
22
22
  group :test do
@@ -15,5 +15,5 @@ Dir['./config/initializers/**/*.rb'].map { |file| require file }
15
15
  Dir['./config/middleware/**/*.rb'].map { |file| require file }
16
16
 
17
17
  # autoload app
18
- relative_load_paths = %w[app/apis app/representers app/models app/workers lib]
18
+ relative_load_paths = %w(app/apis app/representers app/models app/workers lib)
19
19
  ActiveSupport::Dependencies.autoload_paths += relative_load_paths
@@ -6,7 +6,7 @@ require './app'
6
6
  # use Rack::Cors do
7
7
  # allow do
8
8
  # origins '*'
9
- # resource '*', headers: :any, methods: :any
9
+ # resource '*', headers: :any, methods: [:get, :post, :delete, :put, :options]
10
10
  # end
11
11
  # end
12
12
  #
@@ -1,4 +1,5 @@
1
1
  defaults: &defaults
2
+ encoding: <%= @database_encoding %>
2
3
  adapter: <%= @database_adapter %>
3
4
  host: <%%= ENV['DATABASE_HOST'] %>
4
5
  username: <%%= ENV['DATABASE_USER'] %>