punk 0.0.1
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/.document +5 -0
- data/Gemfile +121 -0
- data/Gemfile.lock +353 -0
- data/LICENSE +24 -0
- data/README.md +7 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/bin/punk +18 -0
- data/lib/punk.rb +32 -0
- data/lib/punk/commands/auth.rb +57 -0
- data/lib/punk/commands/generate.rb +54 -0
- data/lib/punk/commands/http.rb +71 -0
- data/lib/punk/commands/list.rb +28 -0
- data/lib/punk/config/console/defaults.json +5 -0
- data/lib/punk/config/defaults.json +47 -0
- data/lib/punk/config/schema.json +55 -0
- data/lib/punk/config/script/defaults.json +5 -0
- data/lib/punk/config/server/development.json +9 -0
- data/lib/punk/config/spec/defaults.json +5 -0
- data/lib/punk/core/app.rb +233 -0
- data/lib/punk/core/boot.rb +9 -0
- data/lib/punk/core/cli.rb +13 -0
- data/lib/punk/core/commander.rb +82 -0
- data/lib/punk/core/commands.rb +26 -0
- data/lib/punk/core/env.rb +290 -0
- data/lib/punk/core/error.rb +10 -0
- data/lib/punk/core/exec.rb +38 -0
- data/lib/punk/core/interface.rb +76 -0
- data/lib/punk/core/load.rb +9 -0
- data/lib/punk/core/logger.rb +33 -0
- data/lib/punk/core/monkey.rb +7 -0
- data/lib/punk/core/monkey_unreloader.rb +18 -0
- data/lib/punk/core/pry.rb +39 -0
- data/lib/punk/core/settings.rb +38 -0
- data/lib/punk/core/version.rb +7 -0
- data/lib/punk/core/worker.rb +13 -0
- data/lib/punk/framework/action.rb +29 -0
- data/lib/punk/framework/all.rb +10 -0
- data/lib/punk/framework/command.rb +117 -0
- data/lib/punk/framework/model.rb +52 -0
- data/lib/punk/framework/plugins/all.rb +3 -0
- data/lib/punk/framework/plugins/validation.rb +55 -0
- data/lib/punk/framework/runnable.rb +31 -0
- data/lib/punk/framework/service.rb +67 -0
- data/lib/punk/framework/view.rb +26 -0
- data/lib/punk/framework/worker.rb +34 -0
- data/lib/punk/helpers/all.rb +8 -0
- data/lib/punk/helpers/loggable.rb +79 -0
- data/lib/punk/helpers/publishable.rb +9 -0
- data/lib/punk/helpers/renderable.rb +75 -0
- data/lib/punk/helpers/swagger.rb +20 -0
- data/lib/punk/helpers/validatable.rb +57 -0
- data/lib/punk/plugins/all.rb +4 -0
- data/lib/punk/plugins/cors.rb +19 -0
- data/lib/punk/plugins/ssl.rb +13 -0
- data/lib/punk/startup/cache.rb +11 -0
- data/lib/punk/startup/database.rb +57 -0
- data/lib/punk/startup/environment.rb +10 -0
- data/lib/punk/startup/logger.rb +20 -0
- data/lib/punk/startup/task.rb +10 -0
- data/lib/punk/templates/fail.jbuilder +4 -0
- data/lib/punk/templates/fail.rcsv +6 -0
- data/lib/punk/templates/fail.slim +9 -0
- data/lib/punk/templates/fail.xml.slim +6 -0
- data/lib/punk/templates/info.jbuilder +3 -0
- data/lib/punk/templates/info.rcsv +2 -0
- data/lib/punk/templates/info.slim +6 -0
- data/lib/punk/templates/info.xml.slim +3 -0
- data/lib/punk/views/all.rb +4 -0
- data/lib/punk/views/fail.rb +21 -0
- data/lib/punk/views/info.rb +20 -0
- data/punk.gemspec +246 -0
- metadata +747 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PUNK
|
4
|
+
class BadRequest < StandardError; end
|
5
|
+
class Unauthorized < StandardError; end # rubocop:disable Layout/EmptyLineBetweenDefs
|
6
|
+
class Forbidden < StandardError; end # rubocop:disable Layout/EmptyLineBetweenDefs
|
7
|
+
class NotFound < StandardError; end # rubocop:disable Layout/EmptyLineBetweenDefs
|
8
|
+
class InternalServerError < StandardError; end # rubocop:disable Layout/EmptyLineBetweenDefs
|
9
|
+
class NotImplemented < StandardError; end # rubocop:disable Layout/EmptyLineBetweenDefs
|
10
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PUNK
|
4
|
+
def self.require_all(path)
|
5
|
+
path = File.expand_path(path)
|
6
|
+
PUNK.profile_debug("require_all", path: path) do
|
7
|
+
if PUNK.get.app.reloadable?
|
8
|
+
PUNK.loader.require(path)
|
9
|
+
else
|
10
|
+
Dir.glob(File.join(path, '**/*.rb')).sort.each { |file| require(file) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
PUNK::Interface.register(:loader) do
|
17
|
+
require 'rack/unreloader'
|
18
|
+
raise PUNK::InternalServerError, "App is not reloadable" unless PUNK.get.app.reloadable?
|
19
|
+
retval = Rack::Unreloader.new { PUNK::App }
|
20
|
+
require_relative './monkey_unreloader'
|
21
|
+
retval
|
22
|
+
end
|
23
|
+
|
24
|
+
PUNK::Interface.register(:app) do
|
25
|
+
require_relative 'app'
|
26
|
+
PUNK.require_all(File.join(PUNK.get.app.path, 'routes'))
|
27
|
+
retval = PUNK.get.app.reloadable ? PUNK.loader : PUNK::App.freeze.app
|
28
|
+
SemanticLogger.flush
|
29
|
+
retval
|
30
|
+
end
|
31
|
+
|
32
|
+
PUNK.inject :loader, :app
|
33
|
+
|
34
|
+
['actions', 'models', 'views', 'services', 'workers'].each do |dir|
|
35
|
+
PUNK.require_all(File.join(PUNK.get.app.path, dir))
|
36
|
+
end
|
37
|
+
|
38
|
+
PUNK.store[:state] = :started
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/dependencies/autoload'
|
4
|
+
require 'active_support/core_ext'
|
5
|
+
require 'active_support/json'
|
6
|
+
|
7
|
+
require 'oj'
|
8
|
+
Oj.optimize_rails
|
9
|
+
|
10
|
+
require 'forwardable'
|
11
|
+
require 'dim'
|
12
|
+
|
13
|
+
require_relative 'version'
|
14
|
+
require_relative 'error'
|
15
|
+
require_relative 'monkey'
|
16
|
+
|
17
|
+
module PUNK
|
18
|
+
extend SingleForwardable
|
19
|
+
|
20
|
+
def_single_delegators :store, :state
|
21
|
+
|
22
|
+
def self.inject(*methods)
|
23
|
+
def_single_delegators :'PUNK::Interface', *methods
|
24
|
+
end
|
25
|
+
|
26
|
+
Interface = Dim::Container.new
|
27
|
+
end
|
28
|
+
|
29
|
+
PUNK::Interface.register(:store) do
|
30
|
+
require 'ostruct'
|
31
|
+
store = OpenStruct.new
|
32
|
+
store.state = :included
|
33
|
+
Thread.current[:rr] = store
|
34
|
+
end
|
35
|
+
|
36
|
+
PUNK::Interface.register(:bootstrap) do
|
37
|
+
raise PUNK::InternalServerError, 'Must call PUNK.init first!' if PUNK.state != :initialised
|
38
|
+
require_relative 'settings'
|
39
|
+
require_relative 'logger'
|
40
|
+
end
|
41
|
+
|
42
|
+
PUNK::Interface.register(:config) do |c|
|
43
|
+
c.bootstrap
|
44
|
+
PUNK.profile_trace('config') do
|
45
|
+
require_relative 'env'
|
46
|
+
PUNK.get.load!
|
47
|
+
require_relative 'commands'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
PUNK::Interface.register(:boot) do |c|
|
52
|
+
c.config
|
53
|
+
retval = PUNK.profile_trace('boot') { require_relative 'boot' }
|
54
|
+
PUNK.logger.info "Punk! v#{PUNK.version}"
|
55
|
+
PUNK.db.fetch("SELECT version(), timeofday()") do |row|
|
56
|
+
row.each_value { |value| PUNK.logger.info value }
|
57
|
+
end
|
58
|
+
retval
|
59
|
+
end
|
60
|
+
|
61
|
+
PUNK::Interface.register(:load) do |c|
|
62
|
+
c.boot
|
63
|
+
PUNK.profile_debug('load') { require_relative 'load' }
|
64
|
+
end
|
65
|
+
|
66
|
+
PUNK::Interface.register(:exec) do |c|
|
67
|
+
c.load
|
68
|
+
retval = PUNK.profile_debug('exec') { require_relative 'exec' }
|
69
|
+
PUNK.logger.tagged(PUNK.env, PUNK.task) do
|
70
|
+
PUNK.logger.info PUNK.get.app.name
|
71
|
+
end
|
72
|
+
SemanticLogger.flush
|
73
|
+
retval
|
74
|
+
end
|
75
|
+
|
76
|
+
PUNK.inject :store, :config, :boot, :load, :exec
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semantic_logger'
|
4
|
+
|
5
|
+
module PUNK
|
6
|
+
include SemanticLogger::Loggable
|
7
|
+
|
8
|
+
def self.profile_info(name, **kwargs)
|
9
|
+
logger.info "Started #{name}", kwargs.sanitize.inspect
|
10
|
+
logger.measure_info("Completed #{name}") { logger.tagged(name) { yield } }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.profile_debug(name, **kwargs)
|
14
|
+
logger.debug "Started #{name}", kwargs.sanitize.inspect
|
15
|
+
logger.measure_debug("Completed #{name}") { logger.tagged(name) { yield } }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.profile_trace(name, **kwargs)
|
19
|
+
logger.trace "Started #{name}", kwargs.sanitize.inspect
|
20
|
+
logger.measure_trace("Completed #{name}") { logger.tagged(name) { yield } }
|
21
|
+
end
|
22
|
+
|
23
|
+
SemanticLogger.default_level =
|
24
|
+
case PUNK.store.args.task
|
25
|
+
when 'console', 'script'
|
26
|
+
:info
|
27
|
+
when 'spec'
|
28
|
+
:debug
|
29
|
+
else
|
30
|
+
:trace
|
31
|
+
end
|
32
|
+
SemanticLogger.add_appender(io: $stdout, formatter: :color)
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# fixes https://github.com/jeremyevans/rack-unreloader/issues/9
|
4
|
+
module Rack
|
5
|
+
class Unreloader
|
6
|
+
class Reloader
|
7
|
+
private
|
8
|
+
|
9
|
+
def all_classes
|
10
|
+
rs = Set.new
|
11
|
+
::ObjectSpace.each_object(Module).each do |mod|
|
12
|
+
rs << mod if !mod.to_s.empty? && monitored_module?(mod)
|
13
|
+
end
|
14
|
+
rs
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
def perform(action_class, **kwargs)
|
4
|
+
raise PUNK::InternalServerError, "Not an action: #{action_class}" unless action_class < PUNK::Action
|
5
|
+
action_class.perform(**kwargs)
|
6
|
+
ensure
|
7
|
+
SemanticLogger.flush
|
8
|
+
end
|
9
|
+
|
10
|
+
def present(view_class, **kwargs)
|
11
|
+
raise PUNK::InternalServerError, "Not a view: #{view_class}" unless view_class < PUNK::View
|
12
|
+
view_class.present(**kwargs)
|
13
|
+
ensure
|
14
|
+
SemanticLogger.flush
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(service_class, **kwargs)
|
18
|
+
raise PUNK::InternalServerError, "Not a service: #{service_class}" unless service_class.superclass == PUNK::Service
|
19
|
+
service_class.run(**kwargs).result
|
20
|
+
ensure
|
21
|
+
SemanticLogger.flush
|
22
|
+
end
|
23
|
+
|
24
|
+
def reload!
|
25
|
+
['actions', 'models', 'views', 'services', 'workers'].each do |dir|
|
26
|
+
path = File.join(PUNK.get.app.path, dir)
|
27
|
+
Dir.glob(File.join(path, '**/*.rb')).each { |file| load(file) }
|
28
|
+
end
|
29
|
+
"Reloaded all actions, models, views, services and workers."
|
30
|
+
ensure
|
31
|
+
SemanticLogger.flush
|
32
|
+
end
|
33
|
+
|
34
|
+
Pry.config.print =
|
35
|
+
proc do |output, value|
|
36
|
+
SemanticLogger.flush
|
37
|
+
output.puts "=> #{value.inspect}"
|
38
|
+
end
|
39
|
+
Pry.config.prompt_name = "🎤 "
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dot_hash'
|
4
|
+
|
5
|
+
module PUNK
|
6
|
+
class Settings < DotHash::Settings
|
7
|
+
alias key? has_key?
|
8
|
+
|
9
|
+
delegate :inspect, to: :_inspect_hash
|
10
|
+
|
11
|
+
def method_missing(key, *args, &block)
|
12
|
+
match = /^(.*)([!?])$/.match(key)
|
13
|
+
if match && key?(match[1]) && !key?(key)
|
14
|
+
value = execute(match[1], *args, &block)
|
15
|
+
case match[2]
|
16
|
+
when '?'
|
17
|
+
return value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
18
|
+
when '!'
|
19
|
+
raise InternalServerError, "Value is nil: #{key}" if value.nil?
|
20
|
+
return value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to_missing?(key, *args)
|
27
|
+
match = /^(.*)([!?])$/.match(key)
|
28
|
+
key = match[1] if match && !key?(key)
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def _inspect_hash
|
35
|
+
to_h.inspect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../punk'
|
4
|
+
require 'sidekiq'
|
5
|
+
require 'sidekiq-cron'
|
6
|
+
|
7
|
+
PUNK.init(task: 'worker', config: { app: { name: 'Norma' } }).exec
|
8
|
+
|
9
|
+
Sidekiq.logger = SemanticLogger['PUNK::SKQ']
|
10
|
+
Sidekiq.logger.class.alias_method(:with_context, :tagged)
|
11
|
+
|
12
|
+
path = File.expand_path(File.join(PUNK.get.app.path, '..', 'config', 'schedule.yml'))
|
13
|
+
Sidekiq::Cron::Job.load_from_hash(YAML.load_file(path))
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PUNK
|
4
|
+
class Action < Service
|
5
|
+
def self.perform(**kwargs)
|
6
|
+
action = profile_info("perform", kwargs) { run(**kwargs) }
|
7
|
+
action.result
|
8
|
+
end
|
9
|
+
|
10
|
+
def process
|
11
|
+
raise NotImplemented, "action must provide process method"
|
12
|
+
end
|
13
|
+
|
14
|
+
def present(view_class, **kwargs)
|
15
|
+
raise InternalServerError, "not a view: #{view_class}" unless view_class < View
|
16
|
+
view_class.run(**kwargs)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def on_success
|
22
|
+
raise InternalServerError, "not a view: #{result}" unless result.is_a?(View)
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_failure
|
26
|
+
@_result = Fail.run(message: "action failed: #{self.class}", error_messages: errors.full_messages, status: invalid? ? 400 : 500)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'plugins/all'
|
4
|
+
require_relative 'model'
|
5
|
+
require_relative 'runnable'
|
6
|
+
require_relative 'service'
|
7
|
+
require_relative 'worker'
|
8
|
+
require_relative 'action'
|
9
|
+
require_relative 'view'
|
10
|
+
require_relative 'command'
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SpecHelpers
|
4
|
+
end
|
5
|
+
|
6
|
+
module PUNK
|
7
|
+
class Command
|
8
|
+
attr_accessor :args, :opts
|
9
|
+
|
10
|
+
def self.create(name, &block)
|
11
|
+
command = new
|
12
|
+
command.send(:_init, name)
|
13
|
+
PUNK.store.commands ||= {}
|
14
|
+
PUNK.store.commands[name] = command
|
15
|
+
command.instance_eval(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def shortcut(name)
|
19
|
+
instance_variable_set(:@shortcut, name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def description(text)
|
23
|
+
instance_variable_set(:@description, text)
|
24
|
+
end
|
25
|
+
|
26
|
+
def option(name:, description:, shortcut: nil, type: String)
|
27
|
+
@options[name] = {
|
28
|
+
name: name,
|
29
|
+
description: description,
|
30
|
+
shortcut: shortcut,
|
31
|
+
type: type
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.pry
|
36
|
+
PUNK.store.commands.each_value do |command|
|
37
|
+
command.send(:_pry)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.commander
|
42
|
+
PUNK.store.commands.each_value do |command|
|
43
|
+
command.send(:_commander)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.spec(scope)
|
48
|
+
PUNK.store.commands.each_value do |command|
|
49
|
+
command.send(:_spec, scope)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def process
|
54
|
+
raise NotImplemented, "command must provide process method"
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def _init(name)
|
60
|
+
@name = name
|
61
|
+
@shortcut = nil
|
62
|
+
@description = nil
|
63
|
+
@options = {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def _pry
|
67
|
+
command = Pry::Commands.create_command(@name) {} # rubocop:disable Lint/EmptyBlock
|
68
|
+
command.description = @description
|
69
|
+
command.instance_variable_set(:@group, "punk")
|
70
|
+
command.class_eval do
|
71
|
+
define_method(:options) do |opt|
|
72
|
+
punk_command = PUNK.store.commands[match]
|
73
|
+
punk_command.instance_variable_get(:@options).each_value do |option|
|
74
|
+
opt.on option[:shortcut], option[:name], option[:description], argument: true, as: option[:type]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
define_method(:process) do
|
78
|
+
punk_command = PUNK.store.commands[match]
|
79
|
+
punk_command.instance_variable_set(:@args, args)
|
80
|
+
punk_command.instance_variable_set(:@opts, opts.to_h)
|
81
|
+
result = punk_command.process
|
82
|
+
SemanticLogger.flush
|
83
|
+
output.puts result
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def _commander
|
89
|
+
command @name do |c|
|
90
|
+
c.description = @description
|
91
|
+
@options.each_value do |option|
|
92
|
+
c.option "-#{option[:shortcut]}", option[:description], "--#{option[:name]} #{option[:type].to_s.upcase}", option[:type]
|
93
|
+
end
|
94
|
+
c.action do |args, opts|
|
95
|
+
@args = args
|
96
|
+
@opts = opts.__hash__
|
97
|
+
PUNK.exec
|
98
|
+
result = process
|
99
|
+
SemanticLogger.flush
|
100
|
+
puts result # rubocop:disable Rails/Output
|
101
|
+
end
|
102
|
+
end
|
103
|
+
return unless @shortcut
|
104
|
+
alias_command @shortcut, @name
|
105
|
+
end
|
106
|
+
|
107
|
+
def _spec(scope)
|
108
|
+
this = self
|
109
|
+
SpecHelpers.send(:define_method, "command_#{@name}") do |*args, **kwargs|
|
110
|
+
this.instance_variable_set(:@args, args)
|
111
|
+
this.instance_variable_set(:@opts, kwargs)
|
112
|
+
this.process
|
113
|
+
end
|
114
|
+
scope.include SpecHelpers
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|