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.
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