rails-service 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +99 -0
- data/.travis.yml +4 -0
- data/Gemfile +11 -0
- data/LICENSE +21 -0
- data/README.md +71 -0
- data/Rakefile +16 -0
- data/app/controller/rails/service/admin_controller.rb +32 -0
- data/app/controller/rails/service/base_controller.rb +3 -0
- data/app/controller/rails/service/status_controller.rb +44 -0
- data/app/helpers/rails/service/admin_helper.rb +27 -0
- data/app/views/layouts/admin.html +100 -0
- data/app/views/rails/service/admin/config.html.erb +3 -0
- data/app/views/rails/service/admin/environment.html.erb +16 -0
- data/app/views/rails/service/admin/manifest.html.erb +1 -0
- data/app/views/rails/service/admin/rails_properties.html.erb +1 -0
- data/app/views/rails/service/admin/routing.html.erb +1 -0
- data/bin/console +8 -0
- data/bin/setup +7 -0
- data/config/routes.rb +9 -0
- data/lib/rails/service.rb +29 -0
- data/lib/rails/service/admin_view_resolver.rb +18 -0
- data/lib/rails/service/app_config.rb +28 -0
- data/lib/rails/service/boot.rb +23 -0
- data/lib/rails/service/config.rb +128 -0
- data/lib/rails/service/context.rb +13 -0
- data/lib/rails/service/core_ext/deep_struct.rb +23 -0
- data/lib/rails/service/engine.rb +20 -0
- data/lib/rails/service/manifest.rb +27 -0
- data/lib/rails/service/version.rb +5 -0
- data/rails-service.gemspec +22 -0
- data/spec/controllers/admin_controller_spec.rb +51 -0
- data/spec/controllers/status_controller_spec.rb +21 -0
- data/spec/fixtures/app-manifest.yaml +7 -0
- data/spec/lib/app_config_spec.rb +35 -0
- data/spec/lib/context_spec.rb +22 -0
- data/spec/lib/manifest_spec.rb +62 -0
- data/spec/rails_app/README.rdoc +28 -0
- data/spec/rails_app/Rakefile +6 -0
- data/spec/rails_app/app-manifest.yaml +6 -0
- data/spec/rails_app/app/assets/images/.keep +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +13 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +15 -0
- data/spec/rails_app/app/controllers/application_controller.rb +5 -0
- data/spec/rails_app/app/controllers/concerns/.keep +0 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/.keep +0 -0
- data/spec/rails_app/app/models/.keep +0 -0
- data/spec/rails_app/app/models/concerns/.keep +0 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/bin/bundle +3 -0
- data/spec/rails_app/bin/rails +4 -0
- data/spec/rails_app/bin/rake +4 -0
- data/spec/rails_app/bin/setup +29 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/app-config.yaml +8 -0
- data/spec/rails_app/config/application.rb +17 -0
- data/spec/rails_app/config/boot.rb +5 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +41 -0
- data/spec/rails_app/config/environments/production.rb +79 -0
- data/spec/rails_app/config/environments/test.rb +42 -0
- data/spec/rails_app/config/initializers/assets.rb +11 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails_app/config/initializers/inflections.rb +16 -0
- data/spec/rails_app/config/initializers/mime_types.rb +4 -0
- data/spec/rails_app/config/initializers/session_store.rb +3 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_app/config/locales/en.yml +23 -0
- data/spec/rails_app/config/routes.rb +4 -0
- data/spec/rails_app/config/secrets.yml +22 -0
- data/spec/rails_app/db/test.sqlite3 +0 -0
- data/spec/rails_app/lib/assets/.keep +0 -0
- data/spec/rails_app/lib/custom_admin_actions.rb +7 -0
- data/spec/rails_app/lib/service/admin/actions.rb +11 -0
- data/spec/rails_app/lib/service/admin/views/foobar.html.erb +1 -0
- data/spec/rails_app/lib/service/status/actions.rb +11 -0
- data/spec/rails_app/log/.keep +0 -0
- data/spec/rails_app/public/404.html +67 -0
- data/spec/rails_app/public/422.html +67 -0
- data/spec/rails_app/public/500.html +66 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/routes_spec.rb +32 -0
- data/spec/spec_helper.rb +16 -0
- metadata +208 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
<table class="table table-bordered">
|
2
|
+
<thead>
|
3
|
+
<tr>
|
4
|
+
<th>Key</th>
|
5
|
+
<th>Value</th>
|
6
|
+
</tr>
|
7
|
+
</thead>
|
8
|
+
<tbody>
|
9
|
+
<% @env.each do |key, value| %>
|
10
|
+
<tr>
|
11
|
+
<td class="key"><%= key %></td>
|
12
|
+
<td class="value"><%= value %></td>
|
13
|
+
</tr>
|
14
|
+
<% end %>
|
15
|
+
</tbody>
|
16
|
+
</table>
|
@@ -0,0 +1 @@
|
|
1
|
+
<pre><%= JSON.pretty_generate(@manifest.to_h) %></pre>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render_rails_iframe(name: "rails-properties", src: "/rails/info/properties") %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render_rails_iframe(name: "rails-routes", src: "/rails/info/routes") %>
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/config/routes.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Rails::Service::Engine.routes.draw do
|
2
|
+
constraints Rails::Service.config.status_routes_constraint do
|
3
|
+
get '/_status(/:action)', to: 'status#index', as: :status
|
4
|
+
end
|
5
|
+
|
6
|
+
constraints Rails::Service.config.admin_routes_constraint do
|
7
|
+
match '/_admin(/:action)', to: 'admin#environment', via: [:get, :post], as: :admin
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rails/service/version'
|
2
|
+
require 'rails/service/config'
|
3
|
+
require 'rails/service/context'
|
4
|
+
require 'rails/service/manifest'
|
5
|
+
require 'rails/service/app_config'
|
6
|
+
|
7
|
+
module Rails
|
8
|
+
module Service
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def config
|
12
|
+
@config ||= Config.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def context
|
16
|
+
@context ||= Context.new(config._for_context)
|
17
|
+
end
|
18
|
+
|
19
|
+
def manifest
|
20
|
+
@manifest ||= Manifest.new(config._for_manifest)
|
21
|
+
end
|
22
|
+
|
23
|
+
def app_config
|
24
|
+
@app_config ||= AppConfig.new(config._for_app_config)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'rails/service/engine'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'action_view/template/resolver'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Service
|
5
|
+
# AdminViewResolver is a custom ActionView resolver which
|
6
|
+
# removes the prefix normally added to path when resolvin templates.
|
7
|
+
# Without it, we can set arbitraty view path for Rails::Service Admin controller
|
8
|
+
class AdminViewResolver < ::ActionView::FileSystemResolver
|
9
|
+
def initialize(path)
|
10
|
+
super(Rails.root.join(path))
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_templates(name, _prefix, partial, details)
|
14
|
+
super(name, '', partial, details)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
require 'rails/service/core_ext/deep_struct'
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
module Service
|
8
|
+
module AppConfig
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def new(options = {})
|
12
|
+
path = options.fetch(:path)
|
13
|
+
logger = options.fetch(:logger)
|
14
|
+
env = options.fetch(:env)
|
15
|
+
|
16
|
+
path = Rails.root.join(path)
|
17
|
+
if File.exist?(path)
|
18
|
+
file = File.read(path)
|
19
|
+
logger.info("loading app config file: #{path}")
|
20
|
+
DeepStruct.new(YAML.load(ERB.new(file).result)[env.to_s])
|
21
|
+
else
|
22
|
+
logger.warn("app config file not found: #{path}")
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rails
|
2
|
+
module Service
|
3
|
+
module Boot
|
4
|
+
module_function
|
5
|
+
|
6
|
+
STATUS_CONTROLLER = 'Rails::Service::StatusController'.freeze
|
7
|
+
|
8
|
+
def lograge(app)
|
9
|
+
return unless defined?(Lograge) && !app.config.lograge.enable
|
10
|
+
|
11
|
+
app.config.lograge.ignore_custom = lambda { |event|
|
12
|
+
!app.config.service.status_logs_enabled && event.payload[:controller] == STATUS_CONTROLLER
|
13
|
+
}
|
14
|
+
|
15
|
+
app.config.lograge.custom_options = lambda { |_event|
|
16
|
+
{ dc: app.config.service.dc, host: app.config.service.host, app: app.config.service.app }
|
17
|
+
}
|
18
|
+
|
19
|
+
Lograge.setup(app)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Service
|
5
|
+
class Config
|
6
|
+
attr_writer :app, :dc, :host
|
7
|
+
|
8
|
+
attr_accessor :process_id
|
9
|
+
|
10
|
+
attr_writer :status_routes_contraint, :admin_routes_contraint,
|
11
|
+
:admin_view_paths, :status_action_modules, :admin_action_modules,
|
12
|
+
:status_logs_enabled, :manifest_path, :logger, :env, :app_config_path
|
13
|
+
|
14
|
+
def app
|
15
|
+
@app ||= begin
|
16
|
+
if Rails::Service.manifest.respond_to?(:app)
|
17
|
+
Rails::Service.manifest.app
|
18
|
+
else
|
19
|
+
Rails.application.class.parent_name.downcase
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def dc
|
25
|
+
@dc ||= (ENV.key?('DATACENTER') ? ENV.fetch('DATACENTER') : 'local').downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def host
|
29
|
+
@host ||= Socket.gethostname
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_id
|
33
|
+
@process_id ||= -> (app, dc, host, pid, _rev) { "#{app}:#{dc}:#{host}:#{pid}:#{SecureRandom.uuid}".freeze }
|
34
|
+
end
|
35
|
+
|
36
|
+
def pid
|
37
|
+
@pid ||= Process.pid
|
38
|
+
end
|
39
|
+
|
40
|
+
def rev
|
41
|
+
@rev ||= File.exist?('REVISION') ? File.read('REVISION') : '(none)'
|
42
|
+
end
|
43
|
+
|
44
|
+
def env
|
45
|
+
@env ||= Rails.env
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger
|
49
|
+
@logger ||= Rails.logger
|
50
|
+
end
|
51
|
+
|
52
|
+
def manifest_path
|
53
|
+
@manifest_path ||= Rails.root.join('app-manifest.yaml')
|
54
|
+
end
|
55
|
+
|
56
|
+
def app_config_path
|
57
|
+
@app_conifg_path ||= Rails.root.join('config/app-config.yaml')
|
58
|
+
end
|
59
|
+
|
60
|
+
def status_logs_enabled
|
61
|
+
@status_logs_enabled ||= false
|
62
|
+
end
|
63
|
+
|
64
|
+
def status_routes_constraint
|
65
|
+
@status_routes_constraint ||= -> (_request) { true }
|
66
|
+
end
|
67
|
+
|
68
|
+
def admin_routes_constraint
|
69
|
+
@admin_routes_constraint ||= -> (request) { request.local? }
|
70
|
+
end
|
71
|
+
|
72
|
+
def admin_action_modules
|
73
|
+
@admin_action_modules ||= [_default_action_module(:admin)]
|
74
|
+
end
|
75
|
+
|
76
|
+
def status_action_modules
|
77
|
+
@status_action_modules ||= [_default_action_module(:status)]
|
78
|
+
end
|
79
|
+
|
80
|
+
def admin_view_paths
|
81
|
+
@admin_view_paths ||= ['lib/service/admin/views']
|
82
|
+
end
|
83
|
+
|
84
|
+
def admin_view_resolvers
|
85
|
+
admin_view_paths.map { |admin_view_path| Rails::Service::AdminViewResolver.new(admin_view_path) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def _for_context
|
89
|
+
{
|
90
|
+
app: app,
|
91
|
+
dc: dc,
|
92
|
+
host: host,
|
93
|
+
env: env,
|
94
|
+
pid: pid,
|
95
|
+
rev: rev,
|
96
|
+
process_id: process_id.call(app, dc, host, pid, rev),
|
97
|
+
logger: logger,
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def _for_manifest
|
102
|
+
{
|
103
|
+
path: manifest_path,
|
104
|
+
logger: logger,
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def _for_app_config
|
109
|
+
{
|
110
|
+
path: app_config_path,
|
111
|
+
logger: logger,
|
112
|
+
env: env,
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def _default_action_module(resource)
|
119
|
+
filename = "service/#{resource}/actions"
|
120
|
+
return unless File.exist?(Rails.root.join("lib/#{filename}.rb"))
|
121
|
+
|
122
|
+
require filename
|
123
|
+
mod = "#{Rails.application.class.parent_name}::Service::#{resource.to_s.capitalize}::Actions"
|
124
|
+
mod.constantize if Kernel.const_defined?(mod)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Service
|
5
|
+
# Context is an object which holds common configuration and informations
|
6
|
+
# about your service/applicaion that your service uses.
|
7
|
+
#
|
8
|
+
# It is very helpful when developing libraries that will be shared between
|
9
|
+
# your services.
|
10
|
+
class Context < OpenStruct
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Cribbed from http://andreapavoni.com/blog/2013/4/create-recursive-openstruct-from-a-ruby-hash/
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class DeepStruct < OpenStruct
|
5
|
+
def initialize(hash = nil)
|
6
|
+
@table = {}
|
7
|
+
@hash_table = {}
|
8
|
+
|
9
|
+
return unless hash
|
10
|
+
|
11
|
+
hash.each do |k, v|
|
12
|
+
k = k.to_s
|
13
|
+
@table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
|
14
|
+
@hash_table[k] = v
|
15
|
+
|
16
|
+
new_ostruct_member(k)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h
|
21
|
+
@hash_table
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails/engine'
|
2
|
+
require 'rails/service'
|
3
|
+
require 'rails/service/context'
|
4
|
+
require 'rails/service/boot'
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
module Service
|
8
|
+
class Engine < Rails::Engine
|
9
|
+
engine_name 'rails-service'
|
10
|
+
isolate_namespace Rails::Service
|
11
|
+
|
12
|
+
config.autoload_paths << File.expand_path('../../../', __FILE__)
|
13
|
+
config.service = Rails::Service.config
|
14
|
+
|
15
|
+
initializer 'rails.service.lograge' do |app|
|
16
|
+
Rails::Service::Boot.lograge(app)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
require 'rails/service/core_ext/deep_struct'
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
module Service
|
8
|
+
module Manifest
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def new(options = {})
|
12
|
+
path = options.fetch(:path)
|
13
|
+
logger = options.fetch(:logger)
|
14
|
+
|
15
|
+
path = Rails.root.join(path)
|
16
|
+
if File.exist?(path)
|
17
|
+
file = File.read(path)
|
18
|
+
logger.info("loading manifest file: #{path}")
|
19
|
+
DeepStruct.new(YAML.load(ERB.new(file).result))
|
20
|
+
else
|
21
|
+
logger.warn("manifest file not found: #{path}")
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rails/service/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rails-service"
|
8
|
+
spec.version = Rails::Service::VERSION
|
9
|
+
spec.authors = ["Łukasz Strzałkowski"]
|
10
|
+
spec.email = ["lukasz.strzalkowski@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Microservices on Rails}
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split("\n")
|
15
|
+
spec.test_files = `git ls-files -- spec/*`.split("\n")
|
16
|
+
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.required_ruby_version = '>= 2.0.0'
|
20
|
+
|
21
|
+
spec.add_dependency "rails", ">= 3.2.6", "< 5"
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Rails::Service::AdminController, type: :controller do
|
4
|
+
routes { Rails::Service::Engine.routes }
|
5
|
+
render_views
|
6
|
+
|
7
|
+
it 'renders environment action' do
|
8
|
+
get :environment
|
9
|
+
expect(response).to have_http_status(:ok)
|
10
|
+
expect(response).to render_template(:environment)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'renders app manifest' do
|
14
|
+
get :manifest
|
15
|
+
expect(response).to have_http_status :ok
|
16
|
+
expect(response).to render_template 'manifest'
|
17
|
+
expect(assigns(:manifest)).to eq Rails::Service.manifest
|
18
|
+
expect(response.body).to include 'fooxample'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'renders app config' do
|
22
|
+
get :config
|
23
|
+
expect(response).to have_http_status :ok
|
24
|
+
expect(response).to render_template 'config'
|
25
|
+
expect(assigns(:config)).to eq Rails::Service.app_config
|
26
|
+
expect(response.body).to include 'foobar'
|
27
|
+
expect(response.body).to_not include 'foobar2'
|
28
|
+
expect(response.body).to include 'foobar-overwritten'
|
29
|
+
expect(response.body).to include 'barfoo'
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'custom resolvers' do
|
33
|
+
describe 'default' do
|
34
|
+
it 'renders custom action included from lib/service/admin/actions' do
|
35
|
+
get :foobar
|
36
|
+
expect(response).to have_http_status :ok
|
37
|
+
expect(response).to render_template 'foobar'
|
38
|
+
expect(assigns(:foo)).to eq 'test'
|
39
|
+
expect(response.body).to include 'OH HAI'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'custom module at custom path (configured in application.rb)' do
|
44
|
+
it 'renders action from custom module' do
|
45
|
+
get :custom
|
46
|
+
expect(response).to have_http_status :ok
|
47
|
+
expect(response.body).to include 'custom action'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|