gris 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +3 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +67 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +203 -0
- data/Guardfile +82 -0
- data/MIT-LICENSE +20 -0
- data/README.md +7 -0
- data/Rakefile +11 -0
- data/bin/gris +5 -0
- data/gris.gemspec +40 -0
- data/lib/gris.rb +37 -0
- data/lib/gris/cli.rb +71 -0
- data/lib/gris/deprecations.rb +9 -0
- data/lib/gris/deprecations/gris_setup.rb +14 -0
- data/lib/gris/generators.rb +3 -0
- data/lib/gris/generators/api_generator.rb +31 -0
- data/lib/gris/generators/migration_generator.rb +228 -0
- data/lib/gris/generators/scaffold_generator.rb +21 -0
- data/lib/gris/generators/templates/api/app/apis/%name_tableize%_endpoint.rb.tt +65 -0
- data/lib/gris/generators/templates/api/app/models/%name_underscore%.rb.tt +2 -0
- data/lib/gris/generators/templates/api/app/presenters/%name_tableize%_presenter.rb.tt +5 -0
- data/lib/gris/generators/templates/api/app/presenters/%name_underscore%_presenter.rb.tt +10 -0
- data/lib/gris/generators/templates/api/spec/apis/%name_tableize%_endpoint_spec.rb.tt +70 -0
- data/lib/gris/generators/templates/api/spec/fabricators/%name_tableize%_fabricator.rb.tt +2 -0
- data/lib/gris/generators/templates/api/spec/models/%name_underscore%_spec.rb.tt +7 -0
- data/lib/gris/generators/templates/create_table_migration/%migration_filename%.rb.tt +19 -0
- data/lib/gris/generators/templates/migration/%migration_filename%.rb.tt +39 -0
- data/lib/gris/generators/templates/scaffold/.Procfile.tt +1 -0
- data/lib/gris/generators/templates/scaffold/.env.test.tt +11 -0
- data/lib/gris/generators/templates/scaffold/.env.tt +10 -0
- data/lib/gris/generators/templates/scaffold/.gitignore.tt +11 -0
- data/lib/gris/generators/templates/scaffold/.rspec.tt +3 -0
- data/lib/gris/generators/templates/scaffold/.rubocop.yml +18 -0
- data/lib/gris/generators/templates/scaffold/.ruby-gemset.tt +1 -0
- data/lib/gris/generators/templates/scaffold/.ruby-version.tt +1 -0
- data/lib/gris/generators/templates/scaffold/Gemfile.tt +33 -0
- data/lib/gris/generators/templates/scaffold/README.md.tt +3 -0
- data/lib/gris/generators/templates/scaffold/Rakefile +9 -0
- data/lib/gris/generators/templates/scaffold/app.rb +57 -0
- data/lib/gris/generators/templates/scaffold/app/apis/application_endpoint.rb +9 -0
- data/lib/gris/generators/templates/scaffold/app/presenters/root_presenter.rb +20 -0
- data/lib/gris/generators/templates/scaffold/config.ru.tt +6 -0
- data/lib/gris/generators/templates/scaffold/config/database.yml.tt +19 -0
- data/lib/gris/generators/templates/scaffold/config/initializers/active_record.rb +4 -0
- data/lib/gris/generators/templates/scaffold/db/schema.rb +11 -0
- data/lib/gris/generators/templates/scaffold/public/errors/404.html +11 -0
- data/lib/gris/generators/templates/scaffold/public/errors/500.html +11 -0
- data/lib/gris/generators/templates/scaffold/spec/apis/cors_spec.rb +31 -0
- data/lib/gris/generators/templates/scaffold/spec/spec_helper.rb +26 -0
- data/lib/gris/generators/templates/scaffold/spec/support/app_helper.rb +3 -0
- data/lib/gris/grape_extensions/crud_helpers.rb +26 -0
- data/lib/gris/identity.rb +50 -0
- data/lib/gris/middleware/app_monitor.rb +17 -0
- data/lib/gris/output_formatters/paginated_presenter.rb +41 -0
- data/lib/gris/output_formatters/presenter.rb +15 -0
- data/lib/gris/rspec_extensions/response_helpers.rb +57 -0
- data/lib/gris/setup.rb +17 -0
- data/lib/gris/version.rb +38 -0
- data/lib/tasks/db.rake +81 -0
- data/lib/tasks/routes.rake +11 -0
- data/spec/generators/api_generator_spec.rb +76 -0
- data/spec/generators/migration_generator_spec.rb +96 -0
- data/spec/generators/scaffold_generator_spec.rb +119 -0
- data/spec/grape_extensions/crud_helpers_spec.rb +5 -0
- data/spec/identity_spec.rb +86 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/spec_generators_helper.rb +11 -0
- data/spec/version_spec.rb +40 -0
- 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,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,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,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
|
data/lib/gris/setup.rb
ADDED
@@ -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
|
data/lib/gris/version.rb
ADDED
@@ -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
|
data/lib/tasks/db.rake
ADDED
@@ -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
|