textualize 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +3 -0
  6. data/CODE_OF_CONDUCT.md +28 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +1 -0
  9. data/README.md +76 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/bin/textualize +7 -0
  13. data/circle.yml +8 -0
  14. data/lib/textualize.rb +33 -0
  15. data/lib/textualize/tasks/helpers/route_hash_creator.rb +62 -0
  16. data/lib/textualize/tasks/helpers/route_hashes.rb +21 -0
  17. data/lib/textualize/tasks/helpers/transformers/two_hundred.rb +30 -0
  18. data/lib/textualize/tasks/helpers/transformers/two_hundred_four.rb +17 -0
  19. data/lib/textualize/tasks/http_backend.rb +59 -0
  20. data/lib/textualize/tasks/new.rb +44 -0
  21. data/lib/textualize/tasks/request_specs.rb +48 -0
  22. data/lib/textualize/tasks/server.rb +35 -0
  23. data/lib/textualize/templates/http_backend/delete.js.erb +10 -0
  24. data/lib/textualize/templates/http_backend/get.js.erb +10 -0
  25. data/lib/textualize/templates/http_backend/module.js +1 -0
  26. data/lib/textualize/templates/http_backend/patch.js.erb +10 -0
  27. data/lib/textualize/templates/http_backend/post.js.erb +10 -0
  28. data/lib/textualize/templates/new/.gitignore +3 -0
  29. data/lib/textualize/templates/new/.ruby-version +1 -0
  30. data/lib/textualize/templates/new/Gemfile +5 -0
  31. data/lib/textualize/templates/new/Gemfile.lock +28 -0
  32. data/lib/textualize/templates/new/Readme.md +10 -0
  33. data/lib/textualize/templates/new/apis/base.raml +26 -0
  34. data/lib/textualize/templates/new/apis/base/routes/items.yaml +8 -0
  35. data/lib/textualize/templates/new/apis/base/routes/orders.yaml +8 -0
  36. data/lib/textualize/templates/new/apis/base/samples/item.json +12 -0
  37. data/lib/textualize/templates/new/apis/base/samples/items.json +34 -0
  38. data/lib/textualize/templates/new/apis/base/samples/order.json +43 -0
  39. data/lib/textualize/templates/new/apis/base/samples/orders.json +70 -0
  40. data/lib/textualize/templates/new/apis/base/schemas/item.json +17 -0
  41. data/lib/textualize/templates/new/apis/base/schemas/order.json +25 -0
  42. data/lib/textualize/templates/new/apis/documentation/authentication.yaml +21 -0
  43. data/lib/textualize/templates/new/apis/documentation/http_statuses.md +26 -0
  44. data/lib/textualize/templates/new/apis/errors.yaml +6 -0
  45. data/lib/textualize/templates/new/apis/resourceTypes/collection-item-ro.yaml +20 -0
  46. data/lib/textualize/templates/new/apis/resourceTypes/collection-item.yaml +20 -0
  47. data/lib/textualize/templates/new/apis/resourceTypes/collection-ro.yaml +18 -0
  48. data/lib/textualize/templates/new/apis/resourceTypes/collection.yaml +18 -0
  49. data/lib/textualize/templates/new/apis/responses/200.yaml +3 -0
  50. data/lib/textualize/templates/new/apis/responses/204.yaml +1 -0
  51. data/lib/textualize/templates/new/apis/responses/400.yaml +8 -0
  52. data/lib/textualize/templates/new/apis/responses/401.yaml +4 -0
  53. data/lib/textualize/templates/new/apis/responses/403.yaml +4 -0
  54. data/lib/textualize/templates/new/apis/responses/404.yaml +4 -0
  55. data/lib/textualize/templates/new/apis/responses/422.yaml +8 -0
  56. data/lib/textualize/templates/new/apis/responses/500.yaml +4 -0
  57. data/lib/textualize/templates/new/gulpfile.js +93 -0
  58. data/lib/textualize/templates/new/package.json +38 -0
  59. data/lib/textualize/templates/new/server/api/v1/items/get.json +1 -0
  60. data/lib/textualize/templates/new/server/api/v1/items/{item_id}/get.json +1 -0
  61. data/lib/textualize/templates/new/server/api/v1/orders/get.json +1 -0
  62. data/lib/textualize/templates/new/server/api/v1/orders/{order_id}/get.json +1 -0
  63. data/lib/textualize/templates/request_specs/delete_collection-item.rb.erb +0 -0
  64. data/lib/textualize/templates/request_specs/get_collection-item.rb.erb +16 -0
  65. data/lib/textualize/templates/request_specs/get_collection.rb.erb +16 -0
  66. data/lib/textualize/templates/request_specs/patch_collection-item.rb.erb +0 -0
  67. data/lib/textualize/templates/request_specs/post_collection.rb.erb +13 -0
  68. data/lib/textualize/version.rb +3 -0
  69. data/sample_item.rb +43 -0
  70. data/textualize.gemspec +41 -0
  71. metadata +259 -0
@@ -0,0 +1,21 @@
1
+ description: |
2
+ OAuth2 is a protocol that lets external apps request authorization to private
3
+ details in a user's account without getting their password. This is preferred
4
+ over Basic Authentication because tokens can be limited to specific types of
5
+ data, and can be revoked by users at any time.
6
+ type: OAuth 2.0
7
+ describedBy:
8
+ headers:
9
+ Authorization:
10
+ description: Used to send a valid OAuth 2 access token.
11
+ type: string
12
+ responses:
13
+ 404:
14
+ description: Unauthorized
15
+ settings:
16
+ authorizationUri: https://app.my-awesome-restaurant.com/login/oauth/authorize
17
+ accessTokenUri: https://app.my-awesome-restaurant.com/login/oauth/access_token
18
+ authorizationGrants: [ code ]
19
+ scopes:
20
+ - "customer"
21
+ - "staff"
@@ -0,0 +1,26 @@
1
+ # HTTP Status Code Summary
2
+
3
+ We use conventional HTTP response codes to indicate success or failure of an
4
+ API request. In general, codes in the 2xx range indicate success, codes in the
5
+ 4xx range indicate an error that resulted from the provided information (e.g. a
6
+ required parameter was missing, a charge failed, etc.), and codes in the 5xx
7
+ range indicate an error with our servers.
8
+
9
+
10
+ | Code | Description |
11
+ |------------------------------------|-------------------------------------------|
12
+ | 200 - OK | Everything worked as expected. |
13
+ | 201 - Created | New resource being created |
14
+ | 400 - Bad Request | Often missing a required parameter. |
15
+ | 401 - Unauthorized | No valid API key provided. |
16
+ | 402 - Request Failed | Parameters were valid but request failed. |
17
+ | 404 - Not Found | The requested item doesn't exist. |
18
+ | 500, 502, 503, 504 - Server Errors | Something went wrong on our end. |
19
+
20
+ # Errors
21
+
22
+ | ATTRIBUTES | Description |
23
+ |--------------------|-----------------------------------------------------------------------------|
24
+ | code | The same as HTTP status code. This can help other clients are easy to read. |
25
+ | details (optional) | List of validation errors. ex: {name: [{error: :blank}]} |
26
+ | message (optional) | A human-readable message giving more details about the error. |
@@ -0,0 +1,6 @@
1
+ 400: !include ./responses/400.yaml
2
+ 401: !include ./responses/401.yaml
3
+ 403: !include ./responses/403.yaml
4
+ 404: !include ./responses/404.yaml
5
+ 422: !include ./responses/422.yaml
6
+ 500: !include ./responses/500.yaml
@@ -0,0 +1,20 @@
1
+ collection-item-ro:
2
+ description: Read only entity representing a <<resourcePathName|!singularize>>
3
+
4
+ get:
5
+ securedBy: [ oauth_2_0: { scopes: [ "customer", "staff" ] } ]
6
+ description: Get the <<resourcePathName|!singularize>> by ID
7
+ responses:
8
+ 200: !include ../responses/200.yaml
9
+
10
+ patch:
11
+ securedBy: [ oauth_2_0: { scopes: [ "staff" ] } ]
12
+ description: Update the <<resourcePathName|!singularize>> by ID
13
+ responses:
14
+ 204: !include ../responses/204.yaml
15
+
16
+ delete:
17
+ securedBy: [ oauth_2_0: { scopes: [ "staff" ] } ]
18
+ description: Remove the <<resourcePathName|!singularize>> by ID
19
+ responses:
20
+ 204: !include ../responses/204.yaml
@@ -0,0 +1,20 @@
1
+ collection-item:
2
+ description: Entity representing a <<resourcePathName|!singularize>>
3
+
4
+ get:
5
+ securedBy: [ oauth_2_0: { scopes: [ "customer", "staff" ] } ]
6
+ description: Get the <<resourcePathName|!singularize>> by ID
7
+ responses:
8
+ 200: !include ../responses/200.yaml
9
+
10
+ patch:
11
+ securedBy: [ oauth_2_0: { scopes: [ "customer", "staff" ] } ]
12
+ description: Update the <<resourcePathName|!singularize>> by ID
13
+ responses:
14
+ 204: !include ../responses/204.yaml
15
+
16
+ delete:
17
+ securedBy: [ oauth_2_0: { scopes: [ "customer", "staff" ] } ]
18
+ description: Remove the <<resourcePathName|!singularize>> by ID
19
+ responses:
20
+ 204: !include ../responses/204.yaml
@@ -0,0 +1,18 @@
1
+ collection-ro:
2
+ description: Read-only collection of available <<resourcePathName>>
3
+
4
+ get:
5
+ securedBy: [ oauth_2_0: { scopes: [ "staff" ] } ]
6
+ responses:
7
+ 200:
8
+ body:
9
+ application/json:
10
+ example: <<exampleCollection>>
11
+
12
+ post:
13
+ securedBy: [ oauth_2_0: { scopes: [ "staff" ] } ]
14
+ body:
15
+ application/json:
16
+ schema: <<resourcePathName|!singularize>>
17
+ responses:
18
+ 200: !include ../responses/200.yaml
@@ -0,0 +1,18 @@
1
+ collection:
2
+ description: Collection of available <<resourcePathName>>
3
+
4
+ get:
5
+ securedBy: [ oauth_2_0: { scopes: [ "customer", "staff" ] } ]
6
+ responses:
7
+ 200:
8
+ body:
9
+ application/json:
10
+ example: <<exampleCollection>>
11
+
12
+ post:
13
+ securedBy: [ oauth_2_0: { scopes: [ "customer", "staff" ] } ]
14
+ body:
15
+ application/json:
16
+ schema: <<resourcePathName|!singularize>>
17
+ responses:
18
+ 200: !include ../responses/200.yaml
@@ -0,0 +1,3 @@
1
+ body:
2
+ application/json:
3
+ example: <<exampleItem>>
@@ -0,0 +1 @@
1
+ description: Success (no body)
@@ -0,0 +1,8 @@
1
+ body:
2
+ application/json:
3
+ example: |
4
+ {
5
+ name: [
6
+ "has already been taken"
7
+ ]
8
+ }
@@ -0,0 +1,4 @@
1
+ body:
2
+ application/json:
3
+ example: |
4
+ { "message": "Unauthorized, please login" }
@@ -0,0 +1,4 @@
1
+ body:
2
+ application/json:
3
+ example: |
4
+ { "message": "Forbidden" }
@@ -0,0 +1,4 @@
1
+ body:
2
+ application/json:
3
+ example: |
4
+ { "message": "Not found" }
@@ -0,0 +1,8 @@
1
+ body:
2
+ application/json:
3
+ example: |
4
+ {
5
+ name: [
6
+ "parameter is required"
7
+ ]
8
+ }
@@ -0,0 +1,4 @@
1
+ body:
2
+ application/json:
3
+ example: |
4
+ { "message": "Server error" }
@@ -0,0 +1,93 @@
1
+ var gulp = require('gulp');
2
+
3
+ gulp.task('default', ['convert-raml-to-json'])
4
+
5
+ gulp.task('mkdir-src', function() {
6
+ var mkdirp = require('mkdirp')
7
+
8
+ mkdirp('./.tmp')
9
+
10
+ return gulp.src('./')
11
+ })
12
+
13
+ var raml = require('gulp-raml');
14
+ var debug = require('gulp-debug');
15
+
16
+ gulp.task('validate-raml', function() {
17
+ return gulp.src('apis/*.raml')
18
+
19
+ .pipe(debug({title: 'checking raml file: '}))
20
+ .pipe(raml())
21
+ .pipe(raml.reporter('default'))
22
+ .pipe(raml.reporter('fail'))
23
+ })
24
+
25
+ var notify = require('gulp-notify');
26
+ var concat = require('gulp-concat');
27
+ var yaml = require('gulp-yaml');
28
+
29
+ gulp.task('convert-raml-to-json', ['validate-raml'], function() {
30
+ return gulp.src('apis/*.raml', { base: './' })
31
+ .pipe(raml2json())
32
+ .pipe(gulp.dest('./.tmp'))
33
+ })
34
+
35
+ function raml2json(options) {
36
+ var path = require('path');
37
+ var gutil = require('gulp-util');
38
+ var through = require('through2');
39
+ var raml2html = require('raml2html');
40
+
41
+ var simplifyMark = function(mark) {
42
+ if (mark) {
43
+ mark.buffer = mark.buffer.split('\n', mark.line + 1)[mark.line].trim();
44
+ }
45
+ }
46
+
47
+ var options = {}
48
+ options.type = 'json'
49
+ options.config = {
50
+ template: function(obj) { return JSON.stringify(obj, null, 2); }
51
+ };
52
+ options.extension = '.json'
53
+
54
+ var stream = through.obj(function(file, enc, done) {
55
+ var fail = function(message) {
56
+ done(new gutil.PluginError('raml2html', message));
57
+ };
58
+ if (file.isBuffer()) {
59
+ var cwd = process.cwd();
60
+ process.chdir(path.resolve(path.dirname(file.path)));
61
+ raml2html.render(file.contents, options.config,
62
+ function(output) {
63
+ process.chdir(cwd);
64
+ stream.push(new gutil.File({
65
+ base: file.base,
66
+ cwd: file.cwd,
67
+ path: gutil.replaceExtension(file.path, options.extension),
68
+ contents: new Buffer(output)
69
+ }));
70
+ done();
71
+ },
72
+ function(error) {
73
+ process.chdir(cwd);
74
+ simplifyMark(error.context_mark);
75
+ simplifyMark(error.problem_mark);
76
+ process.nextTick(function() {
77
+ fail(JSON.stringify(error, null, 2));
78
+ });
79
+ });
80
+ }
81
+ else if (file.isStream()) fail(
82
+ 'Streams are not supported: ' + file.inspect()
83
+ );
84
+ else if (file.isNull()) fail('Input file is null: ' + file.inspect());
85
+ });
86
+
87
+ return stream;
88
+ }
89
+
90
+ function handleError(err) {
91
+ console.error(err.toString());
92
+ this.emit('end');
93
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "api-documentation",
3
+ "version": "0.0.1",
4
+ "description": "RAML documentation and gulp tasks for test/http stub files",
5
+ "main": "gulpfile.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": ""
12
+ },
13
+ "author": "",
14
+ "license": "Copyright by the contributors of the master branch of this repository",
15
+ "bugs": {
16
+ "url": ""
17
+ },
18
+ "homepage": "",
19
+ "dependencies": {
20
+ "gulp": "^3.8.11",
21
+ "gulp-concat": "^2.5.2",
22
+ "gulp-data": "^1.2.0",
23
+ "gulp-debug": "^2.0.1",
24
+ "gulp-load-plugins": "^0.9.0",
25
+ "gulp-notify": "^2.2.0",
26
+ "gulp-raml": "^0.1.3",
27
+ "gulp-template": "^3.0.0",
28
+ "gulp-util": "^3.0.4",
29
+ "gulp-yaml": "^0.2.4",
30
+ "js-yaml": "^3.2.7",
31
+ "raml2html": "^1.6.0",
32
+ "through2": "^0.6.3"
33
+ },
34
+ "devDependencies": {
35
+ "gulp-load-plugins": "^0.9.0",
36
+ "require-dir": "^0.3.0"
37
+ }
38
+ }
@@ -0,0 +1 @@
1
+ {"data":[{"id":"1","type":"item","created_at":"1430407103","updated_at":"1430407180","name":"Hamburger","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/1"}},{"id":"2","type":"item","created_at":"1430407103","updated_at":"1430407180","name":"Cheese burger","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/2"}},{"id":"3","type":"item","created_at":"1430407103","updated_at":"1430407180","name":"French Fries","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/3"}}]}
@@ -0,0 +1 @@
1
+ {"data":{"id":"1","type":"item","name":"Hamburger","created_at":"1430407103","updated_at":"1430407180","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/hamburger/1"}}}
@@ -0,0 +1 @@
1
+ {"data":[{"id":"1","type":"order","created_at":"1430407203","updated_at":"1430407280","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/orders/1","items":{"related":"https://app.my-awesome-restaurant.com/api/v1/orders/1/items","linkage":[{"type":"items","id":"1"},{"type":"items","id":"3"}]}}},{"id":"2","type":"order","created_at":"1430407203","updated_at":"1430407280","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/orders/1","items":{"related":"https://app.my-awesome-restaurant.com/api/v1/orders/2/items","linkage":[{"type":"items","id":"2"},{"type":"items","id":"3"}]}}}],"included":{"items":{"1":{"id":1,"type":"item","created_at":"1430407103","updated_at":"1430407180","name":"Hamburger","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/1"}},"2":{"id":2,"type":"item","created_at":"1430407103","updated_at":"1430407180","name":"Cheese burger","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/2"}},"3":{"id":3,"type":"item","created_at":"1430407103","updated_at":"1430407180","name":"French Fries","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/3"}}}}}
@@ -0,0 +1 @@
1
+ {"data":{"id":"1","type":"order","created_at":"1430407203","updated_at":"1430407280","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/orders/1","items":{"related":"https://app.my-awesome-restaurant.com/api/v1/orders/1/items","linkage":[{"type":"items","id":"1"},{"type":"items","id":"3"}]}}},"included":{"items":{"1":{"id":1,"type":"item","created_at":"1430407103","updated_at":"1430407180","name":"Hamburger","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/1"}},"3":{"id":3,"type":"item","created_at":"1430407103","updated_at":"1430407180","name":"French Fries","links":{"self":"https://app.my-awesome-restaurant.com/api/v1/items/3"}}}}}
@@ -0,0 +1,16 @@
1
+ require 'airborne'
2
+
3
+ describe 'GET <%= url %>' do
4
+ let!(:<%= name %>) { create(:<%= name %>) }
5
+
6
+ before do
7
+ add_header_tokens(<%= secured_by %>)
8
+ end
9
+
10
+ it 'returns expected json' do
11
+ get "<%= url %>"
12
+ expect_status <%= response_code %>
13
+ expect_json_keys <%= body.data.keys %>
14
+ end
15
+ end
16
+
@@ -0,0 +1,16 @@
1
+ require 'airborne'
2
+
3
+ describe 'GET <%= url %>' do
4
+ let!(:<%= name %>) { create_list(:<%= name %>, 3) }
5
+
6
+ before do
7
+ add_header_tokens(<%= secured_by %>)
8
+ end
9
+
10
+ it 'returns expected json' do
11
+ get "<%= url %>"
12
+ expect_status <%= response_code %>
13
+ expect_json_keys <%= body.data.sample.keys %>
14
+ end
15
+ end
16
+
@@ -0,0 +1,13 @@
1
+ require 'airborne'
2
+
3
+ describe 'POST <%= url %>' do
4
+ before do
5
+ add_header_tokens(<%= secured_by %>)
6
+ end
7
+
8
+ it 'returns the saved record' do
9
+ post "<%= url %>"
10
+ expect_status <%= response_code %>
11
+ end
12
+ end
13
+
@@ -0,0 +1,3 @@
1
+ module Textualize
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,43 @@
1
+ {
2
+ verb: 'get',
3
+ url: '/api/v1/items',
4
+ relative_path: '/items',
5
+ type: 'collection-ro',
6
+ secured_by: ['staff'],
7
+ name: 'items',
8
+ response_code: 200,
9
+ body: {
10
+ data: [
11
+ {
12
+ id: '1',
13
+ type: 'item',
14
+ created_at: '1430407103',
15
+ updated_at: '1430407180',
16
+ name: 'Hamburger',
17
+ links: {
18
+ self: 'https://app.my-awesome-restaurant.com/api/v1/items/1'
19
+ }
20
+ },
21
+ {
22
+ id: '2',
23
+ type: 'item',
24
+ created_at: '1430407103',
25
+ updated_at: '1430407180',
26
+ name: 'Cheese burger',
27
+ links: {
28
+ self: 'https://app.my-awesome-restaurant.com/api/v1/items/2'
29
+ }
30
+ },
31
+ {
32
+ id: '3',
33
+ type: 'item',
34
+ created_at: '1430407103',
35
+ updated_at: '1430407180',
36
+ name: 'French Fries',
37
+ links: {
38
+ self: 'https://app.my-awesome-restaurant.com/api/v1/items/3'
39
+ }
40
+ }
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,41 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'textualize/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'textualize'
8
+ spec.version = Textualize::VERSION
9
+ spec.authors = ['Q-Centrix Dev Team']
10
+ spec.email = ['devjobs@q-centrix.com']
11
+
12
+ spec.summary = 'Turn Documentation into Tests and More'
13
+ spec.description = <<-DESCRIPTION
14
+ Textualize uses RAML for documentation and comes with scripts to generate
15
+ template files that can be used to fulfill an implementation of your
16
+ documentation.
17
+ DESCRIPTION
18
+
19
+ spec.homepage = 'https://github.com/q-centrix/textualize'
20
+ spec.license = 'MIT'
21
+
22
+ spec.files = `git ls-files`.split("\n").reject do |file|
23
+ file.match(%r{^(spec|features)/})
24
+ end
25
+ spec.executables = spec.files.grep(%r{^bin/}) do |file|
26
+ File.basename(file)
27
+ end
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_runtime_dependency 'thor', '~> 0'
31
+ spec.add_runtime_dependency 'attr_extras', '~> 4'
32
+ spec.add_runtime_dependency 'hashie', '~> 3'
33
+ spec.add_runtime_dependency 'uglifier', '~> 2'
34
+
35
+ spec.add_development_dependency 'aruba', '~> 0'
36
+ spec.add_development_dependency 'byebug', '~> 3'
37
+ spec.add_development_dependency 'pry', '~> 0'
38
+ spec.add_development_dependency 'bundler', '~> 1.8'
39
+ spec.add_development_dependency 'rake', '~> 10.0'
40
+ spec.add_development_dependency 'rspec', '~> 3'
41
+ end