gris 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +1 -0
  5. data/.rubocop_todo.yml +67 -0
  6. data/.ruby-gemset +1 -0
  7. data/.ruby-version +1 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +203 -0
  10. data/Guardfile +82 -0
  11. data/MIT-LICENSE +20 -0
  12. data/README.md +7 -0
  13. data/Rakefile +11 -0
  14. data/bin/gris +5 -0
  15. data/gris.gemspec +40 -0
  16. data/lib/gris.rb +37 -0
  17. data/lib/gris/cli.rb +71 -0
  18. data/lib/gris/deprecations.rb +9 -0
  19. data/lib/gris/deprecations/gris_setup.rb +14 -0
  20. data/lib/gris/generators.rb +3 -0
  21. data/lib/gris/generators/api_generator.rb +31 -0
  22. data/lib/gris/generators/migration_generator.rb +228 -0
  23. data/lib/gris/generators/scaffold_generator.rb +21 -0
  24. data/lib/gris/generators/templates/api/app/apis/%name_tableize%_endpoint.rb.tt +65 -0
  25. data/lib/gris/generators/templates/api/app/models/%name_underscore%.rb.tt +2 -0
  26. data/lib/gris/generators/templates/api/app/presenters/%name_tableize%_presenter.rb.tt +5 -0
  27. data/lib/gris/generators/templates/api/app/presenters/%name_underscore%_presenter.rb.tt +10 -0
  28. data/lib/gris/generators/templates/api/spec/apis/%name_tableize%_endpoint_spec.rb.tt +70 -0
  29. data/lib/gris/generators/templates/api/spec/fabricators/%name_tableize%_fabricator.rb.tt +2 -0
  30. data/lib/gris/generators/templates/api/spec/models/%name_underscore%_spec.rb.tt +7 -0
  31. data/lib/gris/generators/templates/create_table_migration/%migration_filename%.rb.tt +19 -0
  32. data/lib/gris/generators/templates/migration/%migration_filename%.rb.tt +39 -0
  33. data/lib/gris/generators/templates/scaffold/.Procfile.tt +1 -0
  34. data/lib/gris/generators/templates/scaffold/.env.test.tt +11 -0
  35. data/lib/gris/generators/templates/scaffold/.env.tt +10 -0
  36. data/lib/gris/generators/templates/scaffold/.gitignore.tt +11 -0
  37. data/lib/gris/generators/templates/scaffold/.rspec.tt +3 -0
  38. data/lib/gris/generators/templates/scaffold/.rubocop.yml +18 -0
  39. data/lib/gris/generators/templates/scaffold/.ruby-gemset.tt +1 -0
  40. data/lib/gris/generators/templates/scaffold/.ruby-version.tt +1 -0
  41. data/lib/gris/generators/templates/scaffold/Gemfile.tt +33 -0
  42. data/lib/gris/generators/templates/scaffold/README.md.tt +3 -0
  43. data/lib/gris/generators/templates/scaffold/Rakefile +9 -0
  44. data/lib/gris/generators/templates/scaffold/app.rb +57 -0
  45. data/lib/gris/generators/templates/scaffold/app/apis/application_endpoint.rb +9 -0
  46. data/lib/gris/generators/templates/scaffold/app/presenters/root_presenter.rb +20 -0
  47. data/lib/gris/generators/templates/scaffold/config.ru.tt +6 -0
  48. data/lib/gris/generators/templates/scaffold/config/database.yml.tt +19 -0
  49. data/lib/gris/generators/templates/scaffold/config/initializers/active_record.rb +4 -0
  50. data/lib/gris/generators/templates/scaffold/db/schema.rb +11 -0
  51. data/lib/gris/generators/templates/scaffold/public/errors/404.html +11 -0
  52. data/lib/gris/generators/templates/scaffold/public/errors/500.html +11 -0
  53. data/lib/gris/generators/templates/scaffold/spec/apis/cors_spec.rb +31 -0
  54. data/lib/gris/generators/templates/scaffold/spec/spec_helper.rb +26 -0
  55. data/lib/gris/generators/templates/scaffold/spec/support/app_helper.rb +3 -0
  56. data/lib/gris/grape_extensions/crud_helpers.rb +26 -0
  57. data/lib/gris/identity.rb +50 -0
  58. data/lib/gris/middleware/app_monitor.rb +17 -0
  59. data/lib/gris/output_formatters/paginated_presenter.rb +41 -0
  60. data/lib/gris/output_formatters/presenter.rb +15 -0
  61. data/lib/gris/rspec_extensions/response_helpers.rb +57 -0
  62. data/lib/gris/setup.rb +17 -0
  63. data/lib/gris/version.rb +38 -0
  64. data/lib/tasks/db.rake +81 -0
  65. data/lib/tasks/routes.rake +11 -0
  66. data/spec/generators/api_generator_spec.rb +76 -0
  67. data/spec/generators/migration_generator_spec.rb +96 -0
  68. data/spec/generators/scaffold_generator_spec.rb +119 -0
  69. data/spec/grape_extensions/crud_helpers_spec.rb +5 -0
  70. data/spec/identity_spec.rb +86 -0
  71. data/spec/spec_helper.rb +10 -0
  72. data/spec/support/spec_generators_helper.rb +11 -0
  73. data/spec/version_spec.rb +40 -0
  74. metadata +426 -0
@@ -0,0 +1,19 @@
1
+ defaults: &defaults
2
+ encoding: unicode
3
+ adapter: postgresql
4
+ host: <%%= ENV['DATABASE_HOST'] %>
5
+ username: <%%= ENV['DATABASE_USER'] %>
6
+ password: <%%= ENV['DATABASE_PASSWORD'] %>
7
+ database: <%%= ENV['DATABASE_NAME'] %>
8
+
9
+ production:
10
+ <<: *defaults
11
+
12
+ development:
13
+ <<: *defaults
14
+
15
+ test:
16
+ <<: *defaults
17
+
18
+ staging:
19
+ <<: *defaults
@@ -0,0 +1,4 @@
1
+ require 'erb'
2
+ db = YAML.load(ERB.new(File.read('./config/database.yml')).result)[Gris.env]
3
+ ActiveRecord::Base.establish_connection(db)
4
+ ActiveRecord::Base.include_root_in_json = false
@@ -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.
@@ -0,0 +1,11 @@
1
+ <html>
2
+ <head>
3
+ <title>Page Not Found</title>
4
+ </head>
5
+ <body>
6
+ <h1>Ooops...</h1>
7
+ <p>
8
+ This page could not be found.
9
+ </p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,11 @@
1
+ <html>
2
+ <head>
3
+ <title>Unexpected Error</title>
4
+ </head>
5
+ <body>
6
+ <h1>Ouch...</h1>
7
+ <p>
8
+ Something went terribly wrong.
9
+ </p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Application::Service do
4
+ include Rack::Test::Methods
5
+
6
+ context 'CORS' do
7
+ include_context 'with a running app'
8
+ it 'supports options' do
9
+ options '/', {},
10
+ 'HTTP_ORIGIN' => 'http://cors.example.com',
11
+ 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS' => 'Origin, Accept, Content-Type',
12
+ 'HTTP_ACCESS_CONTROL_REQUEST_METHOD' => 'GET'
13
+
14
+ expect(response_code).to eq 200
15
+ expect(last_response.headers['Access-Control-Allow-Origin']).to eq 'http://cors.example.com'
16
+ expect(last_response.headers['Access-Control-Expose-Headers']).to eq ''
17
+ end
18
+
19
+ it 'includes Access-Control-Allow-Origin in the response' do
20
+ get '/', {}, 'HTTP_ORIGIN' => 'http://cors.example.com'
21
+ expect(response_code).to eq 200
22
+ expect(last_response.headers['Access-Control-Allow-Origin']).to eq 'http://cors.example.com'
23
+ end
24
+
25
+ it 'includes Access-Control-Allow-Origin in errors' do
26
+ get '/invalid', {}, 'HTTP_ORIGIN' => 'http://cors.example.com'
27
+ expect(response_code).to eq 404
28
+ expect(last_response.headers['Access-Control-Allow-Origin']).to eq 'http://cors.example.com'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+
3
+ require 'webmock/rspec'
4
+ require 'rack/test'
5
+ require 'gris/rspec_extensions/response_helpers'
6
+
7
+ require './app'
8
+ # Requires supporting ruby files with custom matchers and macros, etc,
9
+ # in spec/support/ and its subdirectories.
10
+ Dir['./spec/support/**/*.rb'].each { |f| require f }
11
+
12
+ RSpec.configure do |config|
13
+ config.include Gris::RspecExtensions::ResponseHelpers
14
+ config.mock_with :rspec
15
+ config.expect_with :rspec
16
+
17
+ config.before(:suite) do
18
+ ActiveRecord::Migration.maintain_test_schema!
19
+ DatabaseCleaner.strategy = :transaction
20
+ DatabaseCleaner.clean_with(:truncation)
21
+ end
22
+
23
+ config.after(:each) do |_example|
24
+ DatabaseCleaner.clean
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ shared_context 'with a running app' do
2
+ let(:app) { Application::Service.instance }
3
+ end
@@ -0,0 +1,26 @@
1
+ module Gris
2
+ module CrudHelpers
3
+ def create(type, options = {})
4
+ instance = type.create! options[:from]
5
+ present instance, with: options[:with]
6
+ end
7
+
8
+ def update(instance, options = {})
9
+ instance.update_attributes! options[:from]
10
+ present instance, with: options[:with]
11
+ end
12
+
13
+ def delete(instance, options = {})
14
+ instance.destroy
15
+ present instance, with: options[:with]
16
+ end
17
+
18
+ def permitted_params(options = {})
19
+ options = { include_missing: false }.merge(options)
20
+ declared(params, options)
21
+ end
22
+
23
+ # extend all endpoints to include this
24
+ Grape::Endpoint.send :include, self if defined?(Grape)
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ module Gris
2
+ class Identity
3
+ def self.health
4
+ {
5
+ name: name,
6
+ base_url: base_url,
7
+ hostname: hostname,
8
+ revision: revision,
9
+ pid: pid,
10
+ parent_pid: parent_pid,
11
+ platform: platform
12
+ }
13
+ end
14
+
15
+ def self.name
16
+ ENV['SERVICE_NAME'] || 'api-service'
17
+ end
18
+
19
+ def self.base_url
20
+ ENV['BASE_URL'] || 'http://localhost:9292'
21
+ end
22
+
23
+ def self.hostname
24
+ @hostname ||= `hostname`.strip
25
+ end
26
+
27
+ def self.revision
28
+ @revision ||= `git rev-parse HEAD`.strip
29
+ end
30
+
31
+ def self.pid
32
+ @pid ||= Process.pid
33
+ end
34
+
35
+ def self.parent_pid
36
+ @ppid ||= Process.ppid
37
+ end
38
+
39
+ def self.platform
40
+ {
41
+ version: platform_revision,
42
+ name: 'Gris'
43
+ }
44
+ end
45
+
46
+ def self.platform_revision
47
+ Gris::VERSION
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,17 @@
1
+ module Gris
2
+ class Middleware
3
+ class AppMonitor
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ if ['/health', '/health.json'].include? env['PATH_INFO']
10
+ [200, { 'Content-type' => 'application/json' }, [Gris::Identity.health.to_json]]
11
+ else
12
+ @app.call(env)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ module Gris
2
+ module PaginatedPresenter
3
+ def self.included(base)
4
+ base.class_eval do
5
+ include Gris::Presenter
6
+
7
+ property :total_count
8
+ property :total_pages
9
+ property :current_page
10
+ property :next_page
11
+ property :prev_page
12
+
13
+ link :self do |opts|
14
+ "#{request_url(opts)}?#{query_string_for_page(represented.current_page, opts)}"
15
+ end
16
+
17
+ link :next do |opts|
18
+ "#{request_url(opts)}?#{query_string_for_page(represented.next_page, opts)}" if represented.next_page
19
+ end
20
+
21
+ link :prev do |opts|
22
+ "#{request_url(opts)}?#{query_string_for_page(represented.prev_page, opts)}" if represented.prev_page
23
+ end
24
+
25
+ private
26
+
27
+ def request_url(opts)
28
+ request = Grape::Request.new(opts[:env])
29
+ "#{request.base_url}#{opts[:env]['PATH_INFO']}"
30
+ end
31
+
32
+ # replace the page parameter in the query string
33
+ def query_string_for_page(page, opts)
34
+ qs = Rack::Utils.parse_nested_query(opts[:env]['QUERY_STRING'])
35
+ qs['page'] = page
36
+ qs.to_query
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ require 'roar/representer'
2
+ require 'roar/json'
3
+ require 'roar/json/hal'
4
+
5
+ module Gris
6
+ module Presenter
7
+ def self.included(base)
8
+ base.class_eval do
9
+ include Roar::JSON::HAL
10
+ include Roar::Hypermedia
11
+ include Grape::Roar::Representer
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+ module Gris
2
+ module RspecExtensions
3
+ module ResponseHelpers
4
+ def response_code
5
+ last_response.status
6
+ end
7
+
8
+ def response_body
9
+ last_response.body
10
+ end
11
+
12
+ def parsed_response
13
+ Hashie::Mash.new(JSON.parse(last_response.body))
14
+ end
15
+
16
+ def result
17
+ parsed_response
18
+ end
19
+
20
+ def embedded_results(klass)
21
+ parsed_response[:_embedded][klass.name.tableize.to_sym]
22
+ end
23
+
24
+ def embedded_results_count(klass)
25
+ embedded_results(klass).count
26
+ end
27
+
28
+ def expect_embedded_results_count_of(count, klass)
29
+ expect(embedded_results_count(klass)).to eq(count)
30
+ end
31
+
32
+ def first_embedded_result(klass)
33
+ embedded_results(klass).first
34
+ end
35
+
36
+ def embedded_result_with_id(id, klass)
37
+ embedded_results(klass).select { |r| r.id == id }.first
38
+ end
39
+
40
+ def links
41
+ parsed_response['_links']
42
+ end
43
+
44
+ def link_to_self
45
+ links['self']
46
+ end
47
+
48
+ def link_to_next
49
+ links['next']
50
+ end
51
+
52
+ def link_to_previous
53
+ links['prev']
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_support'
2
+
3
+ module Gris
4
+ class << self
5
+ def load_environment
6
+ Dotenv.load(Gris.env.test? ? '.env.test' : '.env')
7
+ end
8
+
9
+ def env
10
+ @_env ||= ActiveSupport::StringInquirer.new(ENV['RACK_ENV'] || 'development')
11
+ end
12
+
13
+ def env=(environment)
14
+ @_env = ActiveSupport::StringInquirer.new(environment)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ module Gris
2
+ VERSION = '0.0.2'
3
+
4
+ class Version
5
+ class << self
6
+ def next_major
7
+ next_level(:major)
8
+ end
9
+
10
+ def next_minor
11
+ next_level(:minor)
12
+ end
13
+
14
+ def next_patch
15
+ next_level(:patch)
16
+ end
17
+
18
+ def next_level(level)
19
+ fail 'Unidentified Level' unless [:major, :minor, :patch].include?(level)
20
+ parts = Gris::VERSION.split('.').map(&:to_i)
21
+ if level == :major
22
+ parts[0] += 1
23
+ significant_index = 1
24
+ end
25
+ if level == :minor
26
+ parts[1] += 1
27
+ significant_index = 2
28
+ end
29
+ if level == :patch
30
+ parts[2] += 1
31
+ significant_index = 3
32
+ end
33
+ parts.map.with_index { |_p, i| parts[i] = 0 if i >= significant_index }
34
+ parts.join('.')
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,81 @@
1
+ unless defined?(Rails)
2
+ task :environment do
3
+ require 'erb'
4
+ require './app'
5
+
6
+ fail 'ActiveRecord Not Found' unless Module.const_defined?(:ActiveRecord)
7
+ end
8
+
9
+ namespace :db do
10
+ desc 'Migrate the database through scripts in db/migrate. Target specific version with VERSION=x'
11
+ task migrate: :environment do
12
+ ActiveRecord::Migrator.migrate('db/migrate', ENV['VERSION'] ? ENV['VERSION'].to_i : nil)
13
+ Rake::Task['db:schema:dump'].invoke unless Gris.env.production?
14
+ end
15
+
16
+ desc 'Rollback to a previous migration. Go back multiple steps with STEP=x'
17
+ task rollback: :environment do
18
+ ActiveRecord::Migration.verbose = true
19
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
20
+ ActiveRecord::Migrator.rollback('db/migrate', step)
21
+ end
22
+
23
+ desc 'Create the database'
24
+ task create: :environment do
25
+ db = YAML.load(ERB.new(File.read('./config/database.yml')).result)[Gris.env]
26
+
27
+ options = {}.tap do |o|
28
+ o[:adapter] = db['adapter']
29
+ o[:database] = 'postgres' if db['adapter'] == 'postgresql'
30
+ end
31
+
32
+ ActiveRecord::Base.establish_connection(options)
33
+ ActiveRecord::Base.connection.create_database(db['database'])
34
+ end
35
+
36
+ desc 'Delete the database'
37
+ task drop: :environment do
38
+ db = YAML.load(ERB.new(File.read('./config/database.yml')).result)[Gris.env]
39
+
40
+ options = {}.tap do |o|
41
+ o[:adapter] = db['adapter']
42
+ o[:database] = 'postgres' if db['adapter'] == 'postgresql'
43
+ end
44
+
45
+ ActiveRecord::Base.establish_connection(options)
46
+ ActiveRecord::Base.connection.drop_database(db['database'])
47
+ end
48
+
49
+ desc 'Create the test database'
50
+ task reset: :environment do
51
+ Rake::Task['db:drop'].invoke
52
+ Rake::Task['db:create'].invoke
53
+ Rake::Task['db:schema:load'].invoke
54
+ end
55
+
56
+ namespace :schema do
57
+ desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR'
58
+ task dump: :environment do
59
+ require 'active_record/schema_dumper'
60
+ File.open(ENV['SCHEMA'] || 'db/schema.rb', 'w') do |file|
61
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
62
+ end
63
+ end
64
+
65
+ desc 'Load a schema.rb file into the database'
66
+ task load: :environment do
67
+ file = ENV['SCHEMA'] || 'db/schema.rb'
68
+ db = YAML.load(ERB.new(File.read('./config/database.yml')).result)[Gris.env]
69
+ ActiveRecord::Base.establish_connection(db)
70
+ ActiveRecord::Schema.verbose = true
71
+ load(file)
72
+ end
73
+ end
74
+
75
+ desc 'Load the seed data from db/seeds.rb'
76
+ task seed: :environment do
77
+ ActiveRecord::Tasks::DatabaseTasks.seed_loader = Gris::ActiveRecordSeeder.new './db/seeds.rb'
78
+ ActiveRecord::Tasks::DatabaseTasks.load_seed
79
+ end
80
+ end
81
+ end