napa 0.1.29 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +46 -121
  4. data/Rakefile +2 -1
  5. data/docs/quickstart.md +292 -0
  6. data/lib/napa/active_record_extensions/stats.rb +46 -0
  7. data/lib/napa/cli.rb +20 -1
  8. data/lib/napa/generators/scaffold_generator.rb +5 -0
  9. data/lib/napa/generators/templates/api/app/apis/%name_tableize%_api.rb.tt +9 -9
  10. data/lib/napa/generators/templates/api/app/representers/%name_underscore%_representer.rb.tt +4 -0
  11. data/lib/napa/generators/templates/scaffold/.env.test.tt +1 -1
  12. data/lib/napa/generators/templates/scaffold/.env.tt +1 -1
  13. data/lib/napa/generators/templates/scaffold/.gitignore.tt +0 -2
  14. data/lib/napa/generators/templates/scaffold/.ruby-gemset.tt +1 -0
  15. data/lib/napa/generators/templates/scaffold/.ruby-version.tt +1 -0
  16. data/lib/napa/generators/templates/scaffold/{Gemfile → Gemfile.tt} +3 -2
  17. data/lib/napa/generators/templates/scaffold/app.rb +1 -1
  18. data/lib/napa/generators/templates/scaffold/config/database.yml.tt +18 -0
  19. data/lib/napa/generators/templates/scaffold/config.ru.tt +3 -0
  20. data/lib/napa/generators/templates/scaffold/db/schema.rb +11 -0
  21. data/lib/napa/generators/templates/scaffold/lib/.keep +0 -0
  22. data/lib/napa/grape_extensions/grape_helpers.rb +20 -0
  23. data/lib/napa/grape_extensions/representer.rb +12 -0
  24. data/lib/napa/middleware/request_stats.rb +3 -3
  25. data/lib/napa/version.rb +1 -1
  26. data/lib/napa.rb +2 -1
  27. data/lib/tasks/db.rake +17 -6
  28. data/lib/tasks/routes.rake +4 -11
  29. data/napa.gemspec +3 -1
  30. data/spec/active_record_extensions/stats_spec.rb +59 -0
  31. metadata +46 -9
  32. data/lib/napa/generators/templates/api/app/entities/%name_underscore%_entity.rb.tt +0 -7
  33. data/lib/napa/generators/templates/scaffold/config/database.yml +0 -18
  34. data/lib/napa/generators/templates/scaffold/console +0 -9
  35. data/lib/napa/grape_extensions/error_presenter.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6b908b554665776da48de53dd708fa5aab073f75
4
- data.tar.gz: 22d74ccfd70d4864121f527266e7de4b062a7068
3
+ metadata.gz: e1c303631113443a38699bded25a0f869222f86a
4
+ data.tar.gz: 33c810492456288eba6f4e0bfc041db253fa8aef
5
5
  SHA512:
6
- metadata.gz: 09c3b747af4ec25f35adbc5f65296139d365907901276c0dfcde2f9ebd8f86243ab55edad8ce83849869dde2f20d3487394ea745a222a79340b698ffc6ba4e4c
7
- data.tar.gz: 4634f451434e794ad7ec3aa3767272e803348e799de9a8412e7cec54320069074ad95c0b03885022e7d36475491228b79fd8e196491458b66bc31b66aeda9e56
6
+ metadata.gz: 3b7bb38860e58f64c4909d5647bb4f3d3ade3fb6f685cd3f0fdbdad72573018d316992068fd346b06d274e8bd3f7d872a6af364348efa699de66932f126c4a5c
7
+ data.tar.gz: 0c85828fa6035881434a92aa4d43d29103476d620233cab74ef951f68e1b3605b9e5cf9b4710a92a9c735f2153d8996f021d4db612f3a9d189182c33f372d50f
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ master
2
+ ===
3
+
4
+ 0.2.0
5
+ ===
6
+ * The console is now run with `napa console`, added support for racksh
7
+ * Scaffold generator now supports the `--database (-d)` flag
8
+ * Scaffold generator now supports Mysql or Postgres with ActiveRecord
9
+ * Scaffold generator now uses Roar instead of Grape Entity
10
+ * Fixed a bug in `rake routes`
11
+ * Fixed a bug in `rake db:reset`
12
+ * Added StatsD instrumentation (experimental)
13
+ * Added a CHANGELOG
data/README.md CHANGED
@@ -3,14 +3,7 @@
3
3
 
4
4
  # Napa
5
5
 
6
- The Napa gem is a simple framework for building APIs with Grape. These features include:
7
-
8
- * Generator
9
- * Console
10
- * Identity
11
- * Logging
12
- * Deployment
13
- * Grape Specific Features (Cache management and Route inspection) i.e. `rake routes`
6
+ Napa is a simple framework for building Rack based APIs using Grape, Roar and ActiveRecord. It's designed to make it easy to quickly create and deploy new API services by providing **generators**, **middlewares** and a **console** similar to what you would expect from a Rails app.
14
7
 
15
8
  ## Installation
16
9
 
@@ -34,73 +27,72 @@ $ bundle install
34
27
 
35
28
  ## Getting Started
36
29
 
37
- Napa comes with a useful generator to quickly scaffold up a new project. Simply run:
38
-
39
- ```
40
- $ napa new your_project_name
41
- ```
30
+ See the [Quickstart Guide](https://github.com/bellycard/napa/blob/master/docs/quickstart.md) for an intro to creating a simple service with Napa.
42
31
 
43
- This will generate a basic application framework for you. It includes everything you need to get started including a Hello World API.
32
+ ## Usage
44
33
 
45
- 1) To get started, run Bundler to make sure you have all the gems for the project:
34
+ Run `napa` terminal prompt to see available features:
46
35
 
47
36
  ```
48
- $ bundle install
37
+ Commands:
38
+ napa console # Start the Napa console
39
+ napa generate api <api_name> # Create a Grape API, Model and Representer
40
+ napa generate migration <migration_name> # Create a Database Migration
41
+ napa help [COMMAND] # Describe available commands or one specific command
42
+ napa new <app_name> [app_path] # Create a scaffold for a new Napa service
43
+ napa version # Shows the Napa version number
49
44
  ```
50
45
 
51
- 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:
52
46
 
53
- ```
54
- $ rake db:create
55
- ```
56
-
57
- 3) Now you're ready to start up the server:
47
+ ### Console
48
+ Similar to the Rails console, load an IRB sesson with your applications environment by running:
58
49
 
59
50
  ```
60
- $ shotgun
51
+ napa console
61
52
  ```
62
53
 
63
- 4) Once the server is started, run the following command to load your service in a browser:
64
-
65
- ```
66
- $ open http://127.0.0.1:9393/hello
67
- ```
54
+ ### Rake Tasks
68
55
 
69
- ...and you should see:
56
+ `rake -T` will give you a list of all available rake tasks:
70
57
 
71
58
  ```
72
- {
73
- message: "Hello Wonderful World!"
74
- }
59
+ rake db:create # Create the database
60
+ rake db:drop # Delete the database
61
+ rake db:migrate # Migrate the database through scripts in db/migrate
62
+ rake db:reset # Create the test database
63
+ rake db:schema:dump # Create a db/schema.rb file that can be portably used against any DB supported by AR
64
+ rake db:schema:load # Load a schema.rb file into the database
65
+ rake deploy:production # Deploy to production
66
+ rake deploy:staging # Deploy to staging
67
+ rake git:set_tag[tag] # Set tag, which triggers deploy
68
+ rake git:verify # Verify git repository is in a good state for deployment
69
+ rake routes # display all routes for Grape
75
70
  ```
76
71
 
77
- 5) We've also provided a sample spec file. You can run the tests by running:
72
+ ## Middlewares
73
+ Napa includes a number of Rack middlewares that can be enabled to add functionality to your project.
78
74
 
79
- ```
80
- RACK_ENV='test' rake db:test:prepare
81
- rspec spec
82
- ```
75
+ ### Authentication
76
+ The Authentication middleware will add a simple header based authentication layer to all requests. This is just looking for a header of `'Password' = 'Your Password'`. The passwords are defined in the `.env` file. You can allow multiple passwords by supplying a comma separated list. For example:
77
+
78
+ `HEADER_PASSWORDS='password1,password2'`
83
79
 
84
- ## Usage/Features
80
+ If your application doesn't require authentication, you can simply remove the middleware.
85
81
 
86
- ### Console
87
- Similar to the Rails console, load an IRB sesson with your applications environment by running:
82
+ ### Health Check
83
+ The Health Check middleware will add an endpoint at `/health` that will return some data about your app. This was created to allow monitoring tools a standardized way to monitor multiple services. This endpoint will return a response similar to this:
88
84
 
89
85
  ```
90
- ruby console
86
+ {
87
+ "name": "service-name",
88
+ "hostname": "host-name",
89
+ "revision": "current-git-sha-of-app",
90
+ "pid": 1234,
91
+ "parent_pid": 1233,
92
+ "napa_revision": "running-version-of-napa"
93
+ }
91
94
  ```
92
95
 
93
- ### Identity
94
- The *Identity* module exists to provide and interface to get information about the application. For example:
95
-
96
- `Napa::Identity.name` => Returns the name of the app defined by `ENV['SERVICE_NAME']`.
97
-
98
- `Napa::Identity.hostname` => Returns the name of the host running the application.
99
-
100
- `Napa::Identity.revision` => Returns the current revision from Git.
101
-
102
- `Napa::Identity.pid` => Returns the current running process id.
103
-
104
96
  ### Logger
105
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:
106
98
 
@@ -120,70 +112,8 @@ ActiveRecord::Base.logger = Napa::Logger.logger
120
112
  Napa::Logger.logger.debug 'Some Debug Message'
121
113
  ```
122
114
 
123
- ### Deployment
124
- 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.
125
-
126
- The tasks currently available are:
127
-
128
- ```ruby
129
- rake deploy:staging
130
- ```
131
-
132
-
133
- ```ruby
134
- rake deploy:production
135
- ```
136
-
137
- **Please Note:** These tasks rely on two environment variables - `GITHUB_OAUTH_TOKEN` and `GITHUB_REPO`. For more information, see **Environment Variables** below.
138
-
139
- ### Grape Specific Features
140
- At Belly we use the [Grape Micro-Framework](https://github.com/intridea/grape) for many services, so we've included a few common features.
141
-
142
- #### Cache Managment
143
- 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.
144
-
145
- #### Route Inspection
146
- A rake task is included to give you a Rails style list of your routes. Simpley run:
147
-
148
- ```ruby
149
- rake routes
150
- ```
151
-
152
- ### Environment Variables
153
- Napa expects to find some environment variables set in your application in order for some features to work. A list is below:
154
-
155
- #### SERVICE_NAME
156
- The name of your app or service, used by Identity and as a label for your logs
157
-
158
- #### GITHUB\_OAUTH\_TOKEN
159
- Used to grant access to your application on Github for deployment tagging
160
-
161
- #### GITHUB_REPO
162
- Your application's Github repo. i.e. `bellycard/napa`
163
-
164
- ## Middlewares
165
- Napa includes a number of Rack middlewares that can be enabled to add functionality to your project.
166
-
167
- ### Authentication
168
- The Authentication middleware will add a simple header based authentication layer to all requests. This is just looking for a header of `'Password' = 'Your Password'`. The passwords are defined in the `.env` file. You can allow multiple passwords by supplying a comma separated list. For example:
169
-
170
- `HEADER_PASSWORDS='password1,password2'`
171
-
172
- If your application doesn't require authentication, you can simply remove the middleware.
173
-
174
- ### Health Check
175
- The Health Check middleware will add an endpoint at `/health` that will return some data about your app. This was created to allow monitoring tools a standardized way to monitor multiple services. This endpoint will return a response similar to this:
176
-
177
- ```
178
- {
179
- "name": "service-name",
180
- "hostname": "host-name",
181
- "revision": "current-git-sha-of-app",
182
- "pid": 1234,
183
- "parent_pid": 1233,
184
- "napa_revision": "running-version-of-napa"
185
- }
186
- ```
115
+ ## Bugs & Feature Requests
116
+ Please add an issue in [Github](https://github.com/bellycard/napa/issues) if you discover a bug or have a feature request.
187
117
 
188
118
  ## Contributing
189
119
 
@@ -192,8 +122,3 @@ The Health Check middleware will add an endpoint at `/health` that will return s
192
122
  3. Commit your changes (`git commit -am 'Added some feature'`)
193
123
  4. Push to the branch (`git push origin my-new-feature`)
194
124
  5. Create new Pull Request
195
-
196
-
197
- ## Todo/Feature Requests
198
-
199
- * Add specs for logger and logging middleware
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env rake
2
2
  Dir.glob('./tasks/*.rake').each { |r| import r }
3
- require "bundler/gem_tasks"
3
+ require 'bundler/gem_tasks'
4
+ require 'napa/active_record_extensions/stats.rb'
@@ -0,0 +1,292 @@
1
+ #Napa Quickstart
2
+
3
+ Napa was designed to make it easy to quickly create a new API service. Here we will cover the steps required to create a new simple API service using Napa, Grape, Roar and ActiveRecord.
4
+
5
+ Napa is available as a Ruby gem, to install it run:
6
+
7
+ ```
8
+ gem install napa
9
+ ```
10
+
11
+ ## Service Scaffold
12
+
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
+
15
+ ```
16
+ napa new people_service
17
+ ```
18
+
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.
20
+
21
+ You will see the following output:
22
+
23
+ ```
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
48
+ Done!
49
+ ```
50
+
51
+ Now, change into the `people_service` directory and run Bundler to get all the gems you need:
52
+
53
+ ```
54
+ bundle install
55
+ ```
56
+
57
+ Once Bundler is done, you can create your databases by running:
58
+
59
+ ```
60
+ rake db:reset
61
+ RACK_ENV=test rake db:reset
62
+ ```
63
+
64
+ These tasks will look into the `.env` and `.env.test` files and drop, create and migrate the databases.
65
+
66
+ Once your databases are setup, you can run `rspec` to verify everything is working. The Napa generator includes a sample API and spec file in it's scaffold.
67
+
68
+ ```
69
+ rspec spec
70
+ ```
71
+
72
+ ## API Generator
73
+
74
+ Now that we have our service scaffolded up, let's generate an API.
75
+
76
+ Napa include an API generator which will create a Grape API, Model and Representer by running:
77
+
78
+ ```
79
+ napa generate api person
80
+ ```
81
+
82
+ **Note:** the generator will pluralize the name of your resource for the API, so use the singular version in the generator.
83
+
84
+ You will see the following output:
85
+
86
+ ```
87
+ Generating api...
88
+ create app/apis/people_api.rb
89
+ create app/models/person.rb
90
+ create app/representers/person_representer.rb
91
+ Done!
92
+ ```
93
+
94
+ ## Create a Person model
95
+
96
+ From the output above, we can see that the generated create a `Person` model, so we should create a migration to actually build the table for that in our database. So, let's run:
97
+
98
+ ```
99
+ napa generate migration CreatePerson
100
+ ```
101
+
102
+ You will see the following output:
103
+
104
+ ```
105
+ Generating migration...
106
+ create db/migrate
107
+ create db/migrate/20140411163743_create_person.rb
108
+ Done!
109
+ ```
110
+
111
+ Open up that migration file and add the migration to create the `people` table:
112
+
113
+ ```ruby
114
+ class CreatePerson < ActiveRecord::Migration
115
+ def change
116
+ create_table :people do |t|
117
+ t.string :name
118
+ t.string :job_title
119
+ t.string :email
120
+ end
121
+ end
122
+ end
123
+ ```
124
+
125
+ Then you can run:
126
+
127
+ ```
128
+ rake db:migrate
129
+ ```
130
+
131
+ ## Declare these attributes in the API and Representer
132
+
133
+ To provide a more secure API, the Napa generator requires you to declare any parameters your API will accept. This is done in the `params` blocks related to the specific request. In this case we will want to declare the params in `people_api.rb` for `POST` and `PUT` to be:
134
+
135
+ ### POST
136
+
137
+ ```ruby
138
+ desc 'Create a person'
139
+ params do
140
+ optional :name, type: String, desc: 'The Name of the person'
141
+ optional :job_title, type: String, desc: 'The Job Title of the person'
142
+ optional :email, type: String, desc: 'The Email Address of the person'
143
+ end
144
+ ```
145
+
146
+ ### PUT
147
+ ```ruby
148
+ desc 'Update a person'
149
+ params do
150
+ optional :name, type: String, desc: 'The Name of the person'
151
+ optional :job_title, type: String, desc: 'The Job Title of the person'
152
+ optional :email, type: String, desc: 'The Email Address of the person'
153
+ end
154
+ ```
155
+
156
+ Then, finally, add these new fields to the `person_representer.rb` so they will be returned from the API response.
157
+
158
+ ```ruby
159
+ class PersonRepresenter < Napa::Representer
160
+ property :id, type: String
161
+ property :name
162
+ property :job_title
163
+ property :email
164
+ end
165
+ ```
166
+
167
+ ## Wire it all up
168
+
169
+ The last thing you'll need to do is mount your new endpoint into the `ApplicationApi`. Your file will look like this:
170
+
171
+ ```ruby
172
+ class ApplicationApi < Grape::API
173
+ format :json
174
+ extend Napa::GrapeExtenders
175
+
176
+ mount PeopleApi => '/people'
177
+
178
+ add_swagger_documentation
179
+ end
180
+ ```
181
+
182
+ Tada, you now have an API!
183
+
184
+ ## Send some requests
185
+
186
+ Now that the code is in place, let's start up the app and send it some requests.
187
+
188
+ Napa runs on Rack, so any rack based webserver will work. Shotgun is nice to use in development because it will reload your app on each request and you don't need to restart the server when there are changes.
189
+
190
+ Start the server on port 9393 by running:
191
+
192
+ ```
193
+ shotgun
194
+ ```
195
+
196
+ ### POST /people
197
+
198
+ To create a person we will send a `POST` request to our API.
199
+
200
+ ```
201
+ curl -X POST -d name="Darby Frey" -d job_title="Software Engineer" -d email="darbyfrey@gmail.com" http://localhost:9393/people
202
+ ```
203
+
204
+ **SIDENOTE:** At this point, you will likely get an error response like the one below. By default Napa ships with the `Napa::Middleware::Authentication` enabled. For the purposes of this guide, we will disable this middleware to make the requests easier to construct. Be sure to re-enable this before you go to production. To disable the middleware, just comment out the `use Napa::Middleware::Authentication` line in `config.ru` and restart shotgun. Also, see the Napa::Middleware::Authentication docs for more details.
205
+
206
+ ```json
207
+ {
208
+ "error": {
209
+ "code": "not_configured",
210
+ "message": "password not configured"
211
+ }
212
+ }
213
+ ```
214
+
215
+
216
+ All response from Napa include the `data` key. In this case the newly created person object is returned nested within the `data` key.
217
+
218
+ ```json
219
+ {
220
+ "data": {
221
+ "object_type": "person",
222
+ "id": "1",
223
+ "name": "Darby Frey",
224
+ "job_title": "Software Engineer",
225
+ "email": "darbyfrey@gmail.com"
226
+ }
227
+ }
228
+ ```
229
+
230
+ ### GET /people
231
+
232
+ ```
233
+ curl -X GET http://localhost:9393/people
234
+ ```
235
+ All response from Napa include the `data` key. In this case, we see an array containing just one user object.
236
+
237
+ ```json
238
+ {
239
+ "data": [
240
+ {
241
+ "id": 1,
242
+ "name": "Darby Frey",
243
+ "job_title": "Software Engineer",
244
+ "email": "darbyfrey@gmail.com"
245
+ }
246
+ ]
247
+ }
248
+ ```
249
+
250
+ ### GET /people/:person_id
251
+
252
+ ```
253
+ curl -X GET http://localhost:9393/people/1
254
+ ```
255
+
256
+ ```json
257
+ {
258
+ "data": {
259
+ "object_type": "person",
260
+ "id": "1",
261
+ "name": "Darby Frey",
262
+ "job_title": "Software Engineer",
263
+ "email": "darbyfrey@gmail.com"
264
+ }
265
+ }
266
+ ```
267
+
268
+ ### PUT /people/:person_id
269
+
270
+ ```
271
+ curl -X PUT -d job_title="Doctor Pepper" http://localhost:9393/people/1
272
+ ```
273
+
274
+ ```json
275
+ {
276
+ "data": {
277
+ "object_type": "person",
278
+ "id": "1",
279
+ "name": "Darby Frey",
280
+ "job_title": "Doctor Pepper",
281
+ "email": "darbyfrey@gmail.com"
282
+ }
283
+ }
284
+ ```
285
+
286
+ So, there you have it, a new API service in minutes. It's very basic, but you can continue to build it out from here. One thing to note, we don't generate a `DELETE` request from the generator, but you can easily add that. The resources section below will link you to the Grape docs where you can find those instructions.
287
+
288
+ ## Resources
289
+
290
+ * [Code Example: people-service](https://github.com/darbyfrey/people_service)
291
+ * [Grape](http://intridea.github.io/grape/)
292
+ * [Roar](https://github.com/apotonick/roar)
@@ -0,0 +1,46 @@
1
+ if defined?(ActiveSupport)
2
+ module Napa
3
+ module ActiveRecordStats
4
+ SQL_INSERT_DELETE_PARSER_REGEXP = /^(?<action>\w+)\s(\w+)\s\W*(?<table>\w+)/
5
+ SQL_SELECT_REGEXP = /select .*? FROM \W*(?<table>\w+)/i
6
+ SQL_UPDATE_REGEXP = /update \W*(?<table>\w+)/i
7
+
8
+ # Returns the table and query type
9
+ def self.extract_from_sql_inserts_deletes(query)
10
+ m = query.match(SQL_INSERT_DELETE_PARSER_REGEXP)
11
+ [m[:table], m[:action]]
12
+ end
13
+
14
+ def self.extract_sql_selects(query)
15
+ m = query.match(SQL_SELECT_REGEXP)
16
+ [m[:table], 'SELECT']
17
+ end
18
+
19
+ def self.guess_sql_content(query)
20
+ m = query.match(SQL_UPDATE_REGEXP)
21
+ return [m[:table], 'UPDATE'] if m
22
+ unless m
23
+ m = query.match(SQL_SELECT_REGEXP)
24
+ extract_sql_selects(query) if m
25
+ end
26
+ end
27
+
28
+ ActiveSupport::Notifications.subscribe 'sql.active_record' do |name, start, finish, id, payload|
29
+ if payload[:name] == 'SQL'
30
+ table, action = extract_from_sql_inserts_deletes(payload[:sql])
31
+ elsif payload[:name] =~ /.* Load$/
32
+ table, action = extract_sql_selects(payload[:sql])
33
+ elsif !payload[:name]
34
+ table, action = guess_sql_content(payload[:sql])
35
+ end
36
+
37
+ if table
38
+ stat_context = "#{Thread.current[:stats_context] || Napa::Identity.name + '.unknown'}"
39
+ Napa::Stats.emitter.timing(
40
+ "#{stat_context}.sql.#{table}.#{action.downcase}.query_time",
41
+ (finish - start) * 1000)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
data/lib/napa/cli.rb CHANGED
@@ -28,6 +28,25 @@ module Napa
28
28
  say Napa::VERSION
29
29
  end
30
30
 
31
+ desc 'console', 'Start the Napa console'
32
+ def console
33
+ require 'racksh/init'
34
+
35
+ begin
36
+ require "pry"
37
+ interpreter = Pry
38
+ rescue LoadError
39
+ require "irb"
40
+ require "irb/completion"
41
+ interpreter = IRB
42
+ end
43
+
44
+ Rack::Shell.init
45
+
46
+ $0 = "#{$0} console"
47
+ interpreter.start
48
+ end
49
+
31
50
  register(
32
51
  Generators::ScaffoldGenerator,
33
52
  'new',
@@ -35,7 +54,7 @@ module Napa
35
54
  'Create a scaffold for a new Napa service'
36
55
  )
37
56
 
38
- desc "generate api <api_name>", "Create a Grape API, Model and Entity"
57
+ desc "generate api <api_name>", "Create a Grape API, Model and Representer"
39
58
  subcommand "generate api", Napa::CLI::Generate
40
59
 
41
60
  desc "generate migration <migration_name>", "Create a Database Migration"
@@ -10,10 +10,15 @@ module Napa
10
10
 
11
11
  argument :app_name
12
12
  argument :app_path, optional: true
13
+ class_option :database, default: 'mysql', aliases: '-d', desc: 'Preconfigure for selected database (options: mysql/postgres/pg)'
13
14
 
14
15
  def generate
15
16
  say 'Generating scaffold...'
16
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'
21
+
17
22
  directory ".", (app_path || app_name)
18
23
 
19
24
  say 'Done!', :green
@@ -1,41 +1,41 @@
1
1
  class <%= name.classify.pluralize %>Api < Grape::API
2
- desc 'Get a list of <%= name.underscore.tableize %>', entity: <%= name.classify %>Entity
2
+ desc 'Get a list of <%= name.underscore.tableize %>'
3
3
  params do
4
4
  optional :ids, type: String, desc: 'comma separated <%= name.underscore %> ids'
5
5
  end
6
6
  get do
7
7
  <%= name.underscore.tableize %> = <%= name.classify %>.filter(declared(params, include_missing: false))
8
- present <%= name.underscore.tableize %>, with: <%= name.classify %>Entity
8
+ represent <%= name.underscore.tableize %>, with: <%= name.classify %>Representer
9
9
  end
10
10
 
11
- desc 'Create an <%= name.underscore %>', entity: <%= name.classify %>Entity
11
+ desc 'Create an <%= name.underscore %>'
12
12
  params do
13
13
  end
14
14
 
15
15
  post do
16
16
  <%= name.underscore %> = <%= name.classify %>.create(declared(params, include_missing: false))
17
17
  error!(present_error(:record_invalid, <%= name.underscore %>.errors.full_messages)) unless <%= name.underscore %>.errors.empty?
18
- present <%= name.underscore %>, with: <%= name.classify %>Entity
18
+ represent <%= name.underscore %>, with: <%= name.classify %>Representer
19
19
  end
20
20
 
21
21
  params do
22
22
  requires :id, desc: 'ID of the <%= name.underscore %>'
23
23
  end
24
24
  route_param :id do
25
- desc 'Get an <%= name.underscore %>', entity: <%= name.classify %>Entity
25
+ desc 'Get an <%= name.underscore %>'
26
26
  get do
27
27
  <%= name.underscore %> = <%= name.classify %>.find(params[:id])
28
- present <%= name.underscore %>, with: <%= name.classify %>Entity
28
+ represent <%= name.underscore %>, with: <%= name.classify %>Representer
29
29
  end
30
30
 
31
- desc 'Update an <%= name.underscore %>', entity: <%= name.classify %>Entity
31
+ desc 'Update an <%= name.underscore %>'
32
32
  params do
33
33
  end
34
34
  put do
35
35
  # fetch <%= name.underscore %> record and update attributes. exceptions caught in app.rb
36
36
  <%= name.underscore %> = <%= name.classify %>.find(params[:id])
37
- <%= name.underscore %>.update_attributes!(declared(params, include_missing: false)
38
- present <%= name.underscore %>, with: <%= name.classify %>Entity
37
+ <%= name.underscore %>.update_attributes!(declared(params, include_missing: false))
38
+ represent <%= name.underscore %>, with: <%= name.classify %>Representer
39
39
  end
40
40
  end
41
41
  end
@@ -0,0 +1,4 @@
1
+ class <%= name.classify %>Representer < Napa::Representer
2
+ property :id, type: String
3
+
4
+ end
@@ -1,7 +1,7 @@
1
1
  SERVICE_NAME=<%= app_name %>
2
2
  RACK_ENV=test
3
3
 
4
- DATABASE_USER=root
4
+ DATABASE_USER='<%= @database_user %>'
5
5
  DATABASE_PASSWORD=''
6
6
  DATABASE_HOST=localhost
7
7
  DATABASE_NAME=<%= app_name.underscore %>_test
@@ -1,6 +1,6 @@
1
1
  SERVICE_NAME=<%= app_name %>
2
2
 
3
- DATABASE_USER=root
3
+ DATABASE_USER='<%= @database_user %>'
4
4
  DATABASE_PASSWORD=''
5
5
  DATABASE_HOST=localhost
6
6
  DATABASE_NAME=<%= app_name.underscore %>_development
@@ -1,5 +1,3 @@
1
- .ruby-gemset
2
- .ruby-version
3
1
  .DS_Store
4
2
  .env
5
3
  .vagrant
@@ -0,0 +1 @@
1
+ <%= app_name %>
@@ -1,13 +1,14 @@
1
1
  source 'https://rubygems.org'
2
+ ruby "2.0.0"
2
3
 
3
4
  gem 'rack-cors'
4
- gem 'mysql2'
5
+ gem '<%= @database_gem %>'
5
6
  gem 'activerecord', '~> 4.0.0', :require => 'active_record'
6
7
  gem 'honeybadger'
7
8
  gem 'json'
8
9
  gem 'shotgun'
9
10
  gem 'napa'
10
- gem 'grape-entity'
11
+ gem 'roar'
11
12
  gem 'grape-swagger'
12
13
 
13
14
  group :development,: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/entities 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
@@ -0,0 +1,18 @@
1
+ defaults: &defaults
2
+ adapter: <%= @database_adapter %>
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
@@ -1,5 +1,8 @@
1
1
  require './app'
2
2
 
3
+ # Experimental StatsD Emitter for ActiveRecord
4
+ # require 'napa/active_record_extensions/stats.rb'
5
+
3
6
  # use Rack::Cors do
4
7
  # allow do
5
8
  # origins '*'
@@ -0,0 +1,11 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your
6
+ # database schema. If you need to create the application database on another
7
+ # system, you should be using db:schema:load, not running all the migrations
8
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
File without changes
@@ -0,0 +1,20 @@
1
+ module Napa
2
+ module GrapeHelpers
3
+ def represent(data, with: nil, **args)
4
+ raise ArgumentError.new(":with option is required") if with.nil?
5
+
6
+ if data.respond_to?(:to_a)
7
+ return { data: data.each{ |item| with.new(item).to_hash(args) } }
8
+ else
9
+ return { data: with.new(data).to_hash(args)}
10
+ end
11
+ end
12
+
13
+ def present_error(code, message = '')
14
+ Napa::JsonError.new(code, message)
15
+ end
16
+
17
+ # extend all endpoints to include this
18
+ Grape::Endpoint.send :include, self
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ require 'roar/decorator'
2
+ require 'roar/representer/json'
3
+ require 'roar/representer/feature/coercion'
4
+
5
+ module Napa
6
+ class Representer < Roar::Decorator
7
+ include Roar::Representer::JSON
8
+ include Representable::Coercion
9
+
10
+ property :object_type, getter: lambda { |*| self.class.name.underscore }
11
+ end
12
+ end
@@ -29,11 +29,11 @@ module Napa
29
29
 
30
30
  request = Rack::Request.new(env)
31
31
  path = normalize_path(request.path_info)
32
- stat = "#{Napa::Identity.name}.http.#{request.request_method.downcase}.#{path}".gsub('/', '.')
32
+ Thread.current[:stats_context] = "#{Napa::Identity.name}.http.#{request.request_method.downcase}.#{path}".gsub('/', '.')
33
33
 
34
34
  # Emit stats to StatsD
35
- Napa::Stats.emitter.increment(stat + '.requests')
36
- Napa::Stats.emitter.timing(stat + '.response_time', response_time)
35
+ Napa::Stats.emitter.increment(Thread.current[:stats_context] + '.requests')
36
+ Napa::Stats.emitter.timing(Thread.current[:stats_context] + '.response_time', response_time)
37
37
  # Return the results
38
38
  [status, headers, body]
39
39
  end
data/lib/napa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Napa
2
- VERSION = '0.1.29'
2
+ VERSION = '0.2.0'
3
3
 
4
4
  class Version
5
5
  class << self
data/lib/napa.rb CHANGED
@@ -17,8 +17,9 @@ require 'napa/json_error'
17
17
  require 'napa/stats'
18
18
  require 'napa/active_record_extensions/filter_by_hash'
19
19
  require 'napa/grape_extensions/error_formatter'
20
- require 'napa/grape_extensions/error_presenter'
20
+ require 'napa/grape_extensions/grape_helpers'
21
21
  require 'napa/grape_extensions/entity'
22
+ require 'napa/grape_extensions/representer'
22
23
  require 'napa/grape_extenders'
23
24
  require 'napa/middleware/logger'
24
25
  require 'napa/middleware/app_monitor'
data/lib/tasks/db.rake CHANGED
@@ -16,17 +16,28 @@ unless defined?(Rails)
16
16
  desc "Create the database"
17
17
  task :create => :environment do
18
18
  db = YAML.load(ERB.new(File.read('./config/database.yml')).result)[Napa.env]
19
- adapter = db.merge({'database'=> 'mysql'})
20
- ActiveRecord::Base.establish_connection(adapter)
21
- ActiveRecord::Base.connection.create_database(db.fetch('database'))
19
+
20
+ options = {}.tap do |o|
21
+ o[:adapter] = db['adapter']
22
+ o[:database] = 'postgres' if db['adapter'] == 'postgres'
23
+ end
24
+
25
+ ActiveRecord::Base.establish_connection(options)
26
+ ActiveRecord::Base.connection.create_database(db['database'])
27
+
22
28
  end
23
29
 
24
30
  desc "Delete the database"
25
31
  task :drop => :environment do
26
32
  db = YAML.load(ERB.new(File.read('./config/database.yml')).result)[Napa.env]
27
- adapter = db.merge({'database'=> 'mysql'})
28
- ActiveRecord::Base.establish_connection(adapter)
29
- ActiveRecord::Base.connection.drop_database(db.fetch('database'))
33
+
34
+ options = {}.tap do |o|
35
+ o[:adapter] = db['adapter']
36
+ o[:database] = 'postgres' if db['adapter'] == 'postgres'
37
+ end
38
+
39
+ ActiveRecord::Base.establish_connection(options)
40
+ ActiveRecord::Base.connection.drop_database(db['database'])
30
41
  end
31
42
 
32
43
  desc "Create the test database"
@@ -1,17 +1,10 @@
1
1
  unless defined?(Rails)
2
2
  desc "display all routes for Grape"
3
3
  task :routes do
4
- grape_apis = ObjectSpace.each_object(Class).select do |klass|
5
- begin
6
- klass < Grape::API
7
- rescue
8
- false
9
- end
10
- end
11
- grape_apis.each do |api|
12
- api.routes.each do |r|
13
- puts "#{r}"
14
- end
4
+ ApplicationApi.routes.each do |api|
5
+ method = api.route_method.ljust(10)
6
+ path = api.route_path
7
+ puts " #{method} #{path}"
15
8
  end
16
9
  end
17
10
  end
data/napa.gemspec CHANGED
@@ -24,13 +24,15 @@ Gem::Specification.new do |gem|
24
24
  gem.add_dependency 'virtus'
25
25
  gem.add_dependency 'grape'
26
26
  gem.add_dependency 'grape-swagger'
27
- gem.add_dependency 'grape-entity'
27
+ gem.add_dependency 'roar'
28
28
  gem.add_dependency 'unicorn'
29
29
  gem.add_dependency 'statsd-ruby'
30
+ gem.add_dependency 'racksh'
30
31
 
31
32
  gem.add_development_dependency 'rspec'
32
33
  gem.add_development_dependency 'pry'
33
34
  gem.add_development_dependency 'git'
34
35
  gem.add_development_dependency 'rubocop'
35
36
  gem.add_development_dependency 'activerecord', '~>3.2.2'
37
+ gem.add_development_dependency 'sqlite3'
36
38
  end
@@ -0,0 +1,59 @@
1
+ require 'active_record'
2
+ require 'spec_helper'
3
+ require 'napa/active_record_extensions/stats.rb'
4
+
5
+ # Delete any prevous instantiations of the emitter and set valid statsd env vars
6
+ Napa::Stats.emitter = nil
7
+ ENV['STATSD_HOST'] = 'localhost'
8
+ ENV['STATSD_PORT'] = '8125'
9
+
10
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
11
+
12
+ ActiveRecord::Schema.define(version: 1) do
13
+ create_table :foos do |t|
14
+ t.string :word
15
+ end
16
+ end
17
+
18
+ class Foo < ActiveRecord::Base
19
+ end
20
+
21
+ describe Napa::ActiveRecordStats do
22
+ before(:each) do
23
+ Foo.delete_all
24
+ @x = Foo.create(word: 'bar')
25
+ end
26
+
27
+ it 'should send a query_time for an insert' do
28
+ Napa::Stats.emitter.should_receive(:timing).with(
29
+ "#{Napa::Identity.name}.unknown.sql.foos.insert.query_time",
30
+ an_instance_of(Float)
31
+ )
32
+ Foo.create(word: 'baz')
33
+ end
34
+
35
+ it 'should send a query_time for a select' do
36
+ Napa::Stats.emitter.should_receive(:timing).with(
37
+ "#{Napa::Identity.name}.unknown.sql.foos.select.query_time",
38
+ an_instance_of(Float)
39
+ )
40
+ Foo.all
41
+ end
42
+
43
+ it 'should send a query_time for a delete' do
44
+ Napa::Stats.emitter.should_receive(:timing).with(
45
+ "#{Napa::Identity.name}.unknown.sql.foos.delete.query_time",
46
+ an_instance_of(Float)
47
+ )
48
+ @x.delete
49
+ end
50
+
51
+ it 'should send a query_time for an update' do
52
+ Napa::Stats.emitter.should_receive(:timing).with(
53
+ "#{Napa::Identity.name}.unknown.sql.foos.update.query_time",
54
+ an_instance_of(Float)
55
+ )
56
+ @x.word = 'baz'
57
+ @x.save
58
+ end
59
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: napa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.29
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darby Frey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-01 00:00:00.000000000 Z
11
+ date: 2014-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -123,7 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: grape-entity
126
+ name: roar
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - '>='
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - '>='
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: racksh
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: rspec
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -234,6 +248,20 @@ dependencies:
234
248
  - - ~>
235
249
  - !ruby/object:Gem::Version
236
250
  version: 3.2.2
251
+ - !ruby/object:Gem::Dependency
252
+ name: sqlite3
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - '>='
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - '>='
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
237
265
  description: A simple framework for building APIs with Grape
238
266
  email:
239
267
  - darby@bellycard.com
@@ -245,13 +273,16 @@ files:
245
273
  - .gitignore
246
274
  - .rubocop.yml
247
275
  - .travis.yml
276
+ - CHANGELOG.md
248
277
  - Gemfile
249
278
  - LICENSE
250
279
  - README.md
251
280
  - Rakefile
252
281
  - bin/napa
282
+ - docs/quickstart.md
253
283
  - lib/napa.rb
254
284
  - lib/napa/active_record_extensions/filter_by_hash.rb
285
+ - lib/napa/active_record_extensions/stats.rb
255
286
  - lib/napa/authentication.rb
256
287
  - lib/napa/cli.rb
257
288
  - lib/napa/generators.rb
@@ -259,24 +290,27 @@ files:
259
290
  - lib/napa/generators/migration_generator.rb
260
291
  - lib/napa/generators/scaffold_generator.rb
261
292
  - lib/napa/generators/templates/api/app/apis/%name_tableize%_api.rb.tt
262
- - lib/napa/generators/templates/api/app/entities/%name_underscore%_entity.rb.tt
263
293
  - lib/napa/generators/templates/api/app/models/%name_underscore%.rb.tt
294
+ - lib/napa/generators/templates/api/app/representers/%name_underscore%_representer.rb.tt
264
295
  - lib/napa/generators/templates/migration/%migration_filename%.rb.tt
265
296
  - lib/napa/generators/templates/scaffold/.env.test.tt
266
297
  - lib/napa/generators/templates/scaffold/.env.tt
267
298
  - lib/napa/generators/templates/scaffold/.gitignore.tt
268
299
  - lib/napa/generators/templates/scaffold/.rubocop.yml
269
- - lib/napa/generators/templates/scaffold/Gemfile
300
+ - lib/napa/generators/templates/scaffold/.ruby-gemset.tt
301
+ - lib/napa/generators/templates/scaffold/.ruby-version.tt
302
+ - lib/napa/generators/templates/scaffold/Gemfile.tt
270
303
  - lib/napa/generators/templates/scaffold/README.md
271
304
  - lib/napa/generators/templates/scaffold/Rakefile
272
305
  - lib/napa/generators/templates/scaffold/app.rb
273
306
  - lib/napa/generators/templates/scaffold/app/apis/application_api.rb
274
307
  - lib/napa/generators/templates/scaffold/app/apis/hello_api.rb.tt
275
308
  - lib/napa/generators/templates/scaffold/config.ru.tt
276
- - lib/napa/generators/templates/scaffold/config/database.yml
309
+ - lib/napa/generators/templates/scaffold/config/database.yml.tt
277
310
  - lib/napa/generators/templates/scaffold/config/initializers/active_record.rb
278
311
  - lib/napa/generators/templates/scaffold/config/middleware/honeybadger.rb
279
- - lib/napa/generators/templates/scaffold/console
312
+ - lib/napa/generators/templates/scaffold/db/schema.rb
313
+ - lib/napa/generators/templates/scaffold/lib/.keep
280
314
  - lib/napa/generators/templates/scaffold/log/.gitkeep
281
315
  - lib/napa/generators/templates/scaffold/spec/apis/hello_api_spec.rb.tt
282
316
  - lib/napa/generators/templates/scaffold/spec/factories/.gitkeep
@@ -284,7 +318,8 @@ files:
284
318
  - lib/napa/grape_extenders.rb
285
319
  - lib/napa/grape_extensions/entity.rb
286
320
  - lib/napa/grape_extensions/error_formatter.rb
287
- - lib/napa/grape_extensions/error_presenter.rb
321
+ - lib/napa/grape_extensions/grape_helpers.rb
322
+ - lib/napa/grape_extensions/representer.rb
288
323
  - lib/napa/identity.rb
289
324
  - lib/napa/json_error.rb
290
325
  - lib/napa/logger/log_transaction.rb
@@ -303,6 +338,7 @@ files:
303
338
  - lib/tasks/routes.rake
304
339
  - napa.gemspec
305
340
  - spec/active_record_extensions/filter_by_hash_spec.rb
341
+ - spec/active_record_extensions/stats_spec.rb
306
342
  - spec/authentication_spec.rb
307
343
  - spec/grape_extensions/error_formatter_spec.rb
308
344
  - spec/identity_spec.rb
@@ -333,12 +369,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
333
369
  version: '0'
334
370
  requirements: []
335
371
  rubyforge_project:
336
- rubygems_version: 2.1.11
372
+ rubygems_version: 2.2.2
337
373
  signing_key:
338
374
  specification_version: 4
339
375
  summary: A simple framework for building APIs with Grape
340
376
  test_files:
341
377
  - spec/active_record_extensions/filter_by_hash_spec.rb
378
+ - spec/active_record_extensions/stats_spec.rb
342
379
  - spec/authentication_spec.rb
343
380
  - spec/grape_extensions/error_formatter_spec.rb
344
381
  - spec/identity_spec.rb
@@ -1,7 +0,0 @@
1
- class <%= name.classify %>Entity < Napa::Entity
2
- root :data, :data
3
- expose :id, proc: lambda { |o, opts| o.id.to_s }, documentation:
4
- { type: 'String', description: 'ID' }
5
- expose :type, proc: lambda { |o, opts| o.class.name.underscore }, documentation:
6
- { type: 'String', description: '"<%= name.underscore %>"' }
7
- end
@@ -1,18 +0,0 @@
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
@@ -1,9 +0,0 @@
1
- require './app'
2
-
3
- begin
4
- require 'pry'
5
- Pry.start
6
- rescue LoadError
7
- require 'irb/completion'
8
- IRB.start
9
- end
@@ -1,10 +0,0 @@
1
- module Napa
2
- module ErrorPresenter
3
- def present_error(code, message = '')
4
- Napa::JsonError.new(code, message)
5
- end
6
-
7
- # extend all endpoints to include this
8
- Grape::Endpoint.send :include, self
9
- end
10
- end