punk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/Gemfile +121 -0
  4. data/Gemfile.lock +353 -0
  5. data/LICENSE +24 -0
  6. data/README.md +7 -0
  7. data/Rakefile +31 -0
  8. data/VERSION +1 -0
  9. data/bin/punk +18 -0
  10. data/lib/punk.rb +32 -0
  11. data/lib/punk/commands/auth.rb +57 -0
  12. data/lib/punk/commands/generate.rb +54 -0
  13. data/lib/punk/commands/http.rb +71 -0
  14. data/lib/punk/commands/list.rb +28 -0
  15. data/lib/punk/config/console/defaults.json +5 -0
  16. data/lib/punk/config/defaults.json +47 -0
  17. data/lib/punk/config/schema.json +55 -0
  18. data/lib/punk/config/script/defaults.json +5 -0
  19. data/lib/punk/config/server/development.json +9 -0
  20. data/lib/punk/config/spec/defaults.json +5 -0
  21. data/lib/punk/core/app.rb +233 -0
  22. data/lib/punk/core/boot.rb +9 -0
  23. data/lib/punk/core/cli.rb +13 -0
  24. data/lib/punk/core/commander.rb +82 -0
  25. data/lib/punk/core/commands.rb +26 -0
  26. data/lib/punk/core/env.rb +290 -0
  27. data/lib/punk/core/error.rb +10 -0
  28. data/lib/punk/core/exec.rb +38 -0
  29. data/lib/punk/core/interface.rb +76 -0
  30. data/lib/punk/core/load.rb +9 -0
  31. data/lib/punk/core/logger.rb +33 -0
  32. data/lib/punk/core/monkey.rb +7 -0
  33. data/lib/punk/core/monkey_unreloader.rb +18 -0
  34. data/lib/punk/core/pry.rb +39 -0
  35. data/lib/punk/core/settings.rb +38 -0
  36. data/lib/punk/core/version.rb +7 -0
  37. data/lib/punk/core/worker.rb +13 -0
  38. data/lib/punk/framework/action.rb +29 -0
  39. data/lib/punk/framework/all.rb +10 -0
  40. data/lib/punk/framework/command.rb +117 -0
  41. data/lib/punk/framework/model.rb +52 -0
  42. data/lib/punk/framework/plugins/all.rb +3 -0
  43. data/lib/punk/framework/plugins/validation.rb +55 -0
  44. data/lib/punk/framework/runnable.rb +31 -0
  45. data/lib/punk/framework/service.rb +67 -0
  46. data/lib/punk/framework/view.rb +26 -0
  47. data/lib/punk/framework/worker.rb +34 -0
  48. data/lib/punk/helpers/all.rb +8 -0
  49. data/lib/punk/helpers/loggable.rb +79 -0
  50. data/lib/punk/helpers/publishable.rb +9 -0
  51. data/lib/punk/helpers/renderable.rb +75 -0
  52. data/lib/punk/helpers/swagger.rb +20 -0
  53. data/lib/punk/helpers/validatable.rb +57 -0
  54. data/lib/punk/plugins/all.rb +4 -0
  55. data/lib/punk/plugins/cors.rb +19 -0
  56. data/lib/punk/plugins/ssl.rb +13 -0
  57. data/lib/punk/startup/cache.rb +11 -0
  58. data/lib/punk/startup/database.rb +57 -0
  59. data/lib/punk/startup/environment.rb +10 -0
  60. data/lib/punk/startup/logger.rb +20 -0
  61. data/lib/punk/startup/task.rb +10 -0
  62. data/lib/punk/templates/fail.jbuilder +4 -0
  63. data/lib/punk/templates/fail.rcsv +6 -0
  64. data/lib/punk/templates/fail.slim +9 -0
  65. data/lib/punk/templates/fail.xml.slim +6 -0
  66. data/lib/punk/templates/info.jbuilder +3 -0
  67. data/lib/punk/templates/info.rcsv +2 -0
  68. data/lib/punk/templates/info.slim +6 -0
  69. data/lib/punk/templates/info.xml.slim +3 -0
  70. data/lib/punk/views/all.rb +4 -0
  71. data/lib/punk/views/fail.rb +21 -0
  72. data/lib/punk/views/info.rb +20 -0
  73. data/punk.gemspec +246 -0
  74. 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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ PUNK.db
4
+
5
+ require_relative '../helpers/all'
6
+ require_relative '../framework/all'
7
+ require_relative '../views/all'
8
+
9
+ PUNK.store[:state] = :loaded
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ def sanitize
5
+ merge(slice(:password, :secret, 'password', 'secret').transform_values { "***" })
6
+ end
7
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ def self.version
5
+ File.read(File.join(__dir__, '..', '..', '..', 'VERSION'))
6
+ end
7
+ 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