boothby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/exe/boothby ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib_path = File.expand_path('../lib', __dir__)
5
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
+ require 'boothby/cli'
7
+
8
+ Signal.trap('INT') do
9
+ warn("\n#{caller.join("\n")}: interrupted")
10
+ exit(1)
11
+ end
12
+
13
+ begin
14
+ Boothby::CLI.start
15
+ rescue Boothby::CLI::Error => e
16
+ puts("ERROR: #{e.message}")
17
+ exit(1)
18
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module Boothby
3
+ class base
4
+
5
+ end
6
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Boothby
6
+ # Handle the application command line parsing
7
+ # and the dispatch to various command objects
8
+ #
9
+ # @api public
10
+ class CLI < Thor
11
+ # Error raised by this runner
12
+ Error = Class.new(StandardError)
13
+
14
+ desc 'version', 'boothby version'
15
+ def version
16
+ require_relative('version')
17
+ puts("v#{Boothby::VERSION}")
18
+ end
19
+ map %w[--version -v] => :version
20
+
21
+ desc 'seed', 'Command description...'
22
+ method_option :help,
23
+ aliases: '-h',
24
+ type: :boolean,
25
+ desc: 'Display usage information'
26
+ def seed(*)
27
+ if options[:help]
28
+ invoke(:help, ['seed'])
29
+ else
30
+ require_relative('commands/seed')
31
+ Boothby::Commands::Seed.new(options).execute
32
+ end
33
+ end
34
+
35
+ desc 'update', 'Command description...'
36
+ method_option :help,
37
+ aliases: '-h',
38
+ type: :boolean,
39
+ desc: 'Display usage information'
40
+ def update(*)
41
+ if options[:help]
42
+ invoke(:help, ['update'])
43
+ else
44
+ require_relative('commands/update')
45
+ Boothby::Commands::Update.new(options).execute
46
+ end
47
+ end
48
+
49
+ desc 'setup', 'Command description...'
50
+ method_option :help,
51
+ aliases: '-h',
52
+ type: :boolean,
53
+ desc: 'Display usage information'
54
+ def setup(*)
55
+ if options[:help]
56
+ invoke(:help, ['setup'])
57
+ else
58
+ require_relative('commands/setup')
59
+ Boothby::Commands::Setup.new(options).execute
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Boothby
6
+ class Command
7
+ extend Forwardable
8
+
9
+ def_delegators :command, :run
10
+
11
+ # Execute this command
12
+ #
13
+ # @api public
14
+ def execute(*)
15
+ raise(
16
+ NotImplementedError,
17
+ "#{self.class}##{__method__} must be implemented"
18
+ )
19
+ end
20
+
21
+ # The external commands runner
22
+ #
23
+ # @see http://www.rubydoc.info/gems/tty-command
24
+ #
25
+ # @api public
26
+ def command(**options)
27
+ require('tty-command')
28
+ TTY::Command.new(options)
29
+ end
30
+
31
+ # The cursor movement
32
+ #
33
+ # @see http://www.rubydoc.info/gems/tty-cursor
34
+ #
35
+ # @api public
36
+ def cursor
37
+ require('tty-cursor')
38
+ TTY::Cursor
39
+ end
40
+
41
+ # Open a file or text in the user's preferred editor
42
+ #
43
+ # @see http://www.rubydoc.info/gems/tty-editor
44
+ #
45
+ # @api public
46
+ def editor
47
+ require('tty-editor')
48
+ TTY::Editor
49
+ end
50
+
51
+ # File manipulation utility methods
52
+ #
53
+ # @see http://www.rubydoc.info/gems/tty-file
54
+ #
55
+ # @api public
56
+ def generator
57
+ require('tty-file')
58
+ TTY::File
59
+ end
60
+
61
+ # Terminal output paging
62
+ #
63
+ # @see http://www.rubydoc.info/gems/tty-pager
64
+ #
65
+ # @api public
66
+ def pager(**options)
67
+ require('tty-pager')
68
+ TTY::Pager.new(options)
69
+ end
70
+
71
+ # Terminal platform and OS properties
72
+ #
73
+ # @see http://www.rubydoc.info/gems/tty-pager
74
+ #
75
+ # @api public
76
+ def platform
77
+ require('tty-platform')
78
+ TTY::Platform.new
79
+ end
80
+
81
+ # The interactive prompt
82
+ #
83
+ # @see http://www.rubydoc.info/gems/tty-prompt
84
+ #
85
+ # @api public
86
+ def prompt(**options)
87
+ require('tty-prompt')
88
+ TTY::Prompt.new(options)
89
+ end
90
+
91
+ # Get terminal screen properties
92
+ #
93
+ # @see http://www.rubydoc.info/gems/tty-screen
94
+ #
95
+ # @api public
96
+ def screen
97
+ require('tty-screen')
98
+ TTY::Screen
99
+ end
100
+
101
+ # The unix which utility
102
+ #
103
+ # @see http://www.rubydoc.info/gems/tty-which
104
+ #
105
+ # @api public
106
+ def which(*args)
107
+ require('tty-which')
108
+ TTY::Which.which(*args)
109
+ end
110
+
111
+ # Check if executable exists
112
+ #
113
+ # @see http://www.rubydoc.info/gems/tty-which
114
+ #
115
+ # @api public
116
+ def exec_exist?(*args)
117
+ require('tty-which')
118
+ TTY::Which.exist?(*args)
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../app'
5
+
6
+ module Boothby
7
+ module Commands
8
+ class Seed < Boothby::Command
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def execute(input: $stdin, output: $stdout)
14
+ Boothby::Seed.seed!
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../app'
5
+
6
+ module Boothby
7
+ module Commands
8
+ class Setup < Boothby::Command
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def execute(input: $stdin, output: $stdout)
14
+ Boothby::App.setup
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../app'
5
+
6
+ module Boothby
7
+ module Commands
8
+ class Update < Boothby::Command
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def execute(input: $stdin, output: $stdout)
14
+ Boothby::App.update
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Boothby
4
+ module Configuration
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ add_config :root
9
+ add_config :seeds_configuration
10
+ add_config :configuration_directory
11
+
12
+ # set default values
13
+ reset_config
14
+ end
15
+
16
+ module ClassMethods
17
+ def add_config(name)
18
+ class_eval(<<-METHODS, __FILE__, __LINE__ + 1)
19
+ @#{name} = nil
20
+ def self.#{name}(value=nil)
21
+ @#{name} = value if value
22
+ return @#{name} if self.object_id == #{object_id} || defined?(@#{name})
23
+ name = superclass.#{name}
24
+ return nil if name.nil? && !instance_variable_defined?(:@#{name})
25
+ @#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
26
+ end
27
+
28
+ def self.#{name}=(value)
29
+ @#{name} = value
30
+ end
31
+
32
+ def #{name}=(value)
33
+ @#{name} = value
34
+ end
35
+
36
+ def #{name}
37
+ value = @#{name} if instance_variable_defined?(:@#{name})
38
+ value = self.class.#{name} unless instance_variable_defined?(:@#{name})
39
+ if value.instance_of?(Proc)
40
+ value.arity >= 1 ? value.call(self) : value.call
41
+ else
42
+ value
43
+ end
44
+ end
45
+ METHODS
46
+ end
47
+
48
+ def configure
49
+ yield(self)
50
+ end
51
+
52
+ def reset_config
53
+ configure do |config|
54
+ config.root = -> { Rails.root }
55
+ config.seeds_configuration = 'db/seeds.yml'
56
+ config.configuration_directory = 'config'
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative 'yaml_reader'
5
+
6
+ # Store configuration YAML files
7
+ module Boothby
8
+ class KeyRing
9
+ include Singleton
10
+
11
+ def initialize
12
+ config_files = Dir[Rails.root.join('config/*.y{a,}ml{.erb,}')]
13
+ @configs = config_files.to_h do |config|
14
+ config_name = File.basename(config).split('.').first.to_sym
15
+ [config_name, Boothby::YAMLReader.new(config)]
16
+ end
17
+ end
18
+
19
+ def self.method_missing(method_name, *arguments, **kwargs, &block)
20
+ if instance.respond_to?(method_name)
21
+ instance.public_send(method_name)
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def self.respond_to_missing?(method_name, include_private = false)
28
+ instance.respond_to?(method_name) || super
29
+ end
30
+
31
+ def method_missing(method_name, *arguments, **kwargs, &block)
32
+ if @configs.key?(method_name.to_sym)
33
+ @configs[method_name.to_sym]
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def respond_to_missing?(method_name, include_private = false)
40
+ @configs.key?(method_name.to_sym) || super
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'tty-command'
5
+
6
+ module Boothby
7
+ class Toolbox
8
+ class << self
9
+ attr_writer :root
10
+ end
11
+
12
+ def self.setup_application
13
+ FileUtils.chdir(app_dir) do
14
+ copy_example_files
15
+ install_dependencies
16
+ rake_task('db:prepare', 'db:test:prepare', label: 'Preparing database')
17
+ rake_task('log:clear', 'tmp:clear', label: 'Cleaning old files')
18
+ rake_task('restart', label: 'Restarting application server')
19
+ end
20
+ end
21
+
22
+ def self.update_application
23
+ FileUtils.chdir(app_dir) do
24
+ copy_example_files
25
+ install_dependencies
26
+ rake_task('db:migrate', label: 'Updating database')
27
+ rake_task('log:clear', 'tmp:clear', label: 'Cleaning old files')
28
+ rake_task('restart', label: 'Restarting application server')
29
+ end
30
+ end
31
+
32
+ def self.upgrade_application
33
+ FileUtils.chdir(app_dir) do
34
+ copy_example_files
35
+ install_dependencies
36
+ rake_task('db:migrate', label: 'Updating database')
37
+ rake_task('log:clear', 'tmp:clear', label: 'Cleaning old files')
38
+ rake_task('restart', label: 'Restarting application server')
39
+ end
40
+ end
41
+
42
+ private_class_method def self.install_dependencies
43
+ puts("\n== Installing dependencies ==")
44
+ bundle_install
45
+ yarn_install
46
+ end
47
+
48
+ private_class_method def self.yarn_install
49
+ return unless File.exist?('package.json')
50
+
51
+ system('command', '-v', 'yarn') || system('npm', 'install', '-g', 'yarn')
52
+ run!(:yarn, :install)
53
+ end
54
+
55
+ private_class_method def self.bundle_install
56
+ return unless File.exist?('Gemfile')
57
+
58
+ system('bundle', 'check') || run!(:bundle, :install)
59
+ end
60
+
61
+ private_class_method def self.copy_example_files
62
+ example_files = Dir['**/*.example*'].filter_map do |example_file|
63
+ real_file = example_file.gsub('.example', '')
64
+ [example_file, real_file] unless File.exist?(real_file)
65
+ end
66
+
67
+ return unless example_files.any?
68
+
69
+ puts("\n== Copying example files ==")
70
+ example_files.each { |ef, rf| run(:cp, ef, rf) }
71
+ end
72
+
73
+ private_class_method def self.rake_task(*args, label:)
74
+ puts("\n== #{label} ==")
75
+ run(:bundle, :exec, :rails, *args)
76
+ end
77
+
78
+ private_class_method def self.run(*args, **kwargs)
79
+ kwargs[:only_output_on_error] = true
80
+ cmd.run!(*args, **kwargs)
81
+ end
82
+
83
+ private_class_method def self.run!(*args, **kwargs)
84
+ kwargs[:only_output_on_error] = false
85
+ cmd.run!(*args, **kwargs)
86
+ end
87
+
88
+ private_class_method def self.cmd
89
+ @cmd ||= TTY::Command.new(
90
+ uuid: false,
91
+ color: true
92
+ )
93
+ end
94
+
95
+ private_class_method def self.app_dir
96
+ (@root || File.expand_path('../..', __dir__))
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-spinner'
4
+
5
+ module Boothby
6
+ class Plant
7
+ class << self
8
+ attr_writer :root, :config_file, :rails_env
9
+ end
10
+
11
+ def self.seed!
12
+ require_seed_modules!
13
+
14
+ seed(environment: :base)
15
+ seed(environment: rails_env)
16
+ end
17
+
18
+ private_class_method def self.seed(environment:)
19
+ label = environment == :base ? 'ALL ENVIRONMENTS' : environment.to_s.upcase
20
+ env_class = environment.to_s.classify
21
+ multi_spinner = TTY::Spinner::Multi.new(
22
+ "[:spinner] SEEDING MODELS FOR #{label}",
23
+ hide_cursor: true,
24
+ success_mark: pastel.green('✓'),
25
+ error_mark: pastel.red('×'),
26
+ interval: 5,
27
+ format: :arc,
28
+ style: {
29
+ top: ' ',
30
+ middle: ' ',
31
+ bottom: ' '
32
+ }
33
+ )
34
+
35
+ config(environment).each do |seed_class|
36
+ seed_from(seed_class)
37
+ end
38
+ multi_spinner.auto_spin
39
+ end
40
+
41
+ private_class_method def self.root(*args)
42
+ @root ||= File.expand_path('../..', __dir__)
43
+ File.join(@root, *args)
44
+ end
45
+
46
+ private_class_method def self.rails_env
47
+ (@rails_env || 'development').to_sym
48
+ end
49
+
50
+ private_class_method def self.seeds_available?(*path)
51
+ Dir[root(:db, :seeds, *path, '*.rb"')].count > 0
52
+ end
53
+
54
+ private_class_method def self.config_file
55
+ (@config_file || root('db/seeds.yml'))
56
+ end
57
+
58
+ private_class_method def self.require_modules(*path)
59
+ Dir[root(:db, :seeds, *path, '*.rb"')].sort.each { |s| require(s) }
60
+ end
61
+
62
+ private_class_method def self.config
63
+ @config ||= begin
64
+ content = File.read(config_file)
65
+ yml = YAML.safe_load(content, [Time, Date], aliases: true)
66
+ yml.with_indifferent_access
67
+ end
68
+ end
69
+
70
+ def self.seed_from(seed_class)
71
+ multi_spinner.register(
72
+ '[:spinner] :title',
73
+ hide_cursor: true,
74
+ success_mark: pastel.green('✓'),
75
+ error_mark: pastel.red('×'),
76
+ interval: 5,
77
+ format: :arc
78
+ ) do |spinner|
79
+ title = seed_method[4..-1].titleize
80
+ spinner.update(title: "Seeding #{title}")
81
+ seed_class.public_send(seed_method)
82
+ spinner.update(title: "Seeded #{title}")
83
+ spinner.success
84
+ end
85
+ end
86
+
87
+ def self.seed_methods(seed_class)
88
+ methods = seed_class.methods.map(&:to_s).select do |method|
89
+ method.starts_with?('seed_')
90
+ end
91
+ methods.sort_by { |method| seed_class.method(method).source_location.last }
92
+ end
93
+ end
94
+ end
95
+
96
+ # :nodoc:
97
+ module Seeds
98
+ # rubocop:disable Style/OpenStructUse
99
+ def self.departments
100
+ [
101
+ Department.master_department,
102
+ Department.executive_health,
103
+ Department.signature_care,
104
+ OpenStruct.new(name: 'General', key: 'general')
105
+ ]
106
+ end
107
+ # rubocop:enable Style/OpenStructUse
108
+
109
+ def self.sprout
110
+ return if Rails.env.test?
111
+
112
+ require_seed_modules!
113
+
114
+ seed_by_departments(environment: :base)
115
+ seed_by_departments(environment: Rails.env.to_sym)
116
+ end
117
+
118
+ def self.seed(environment:)
119
+ label = environment == :base ? 'ALL ENVIRONMENTS' : environment.to_s.upcase
120
+ env_class = environment.to_s.classify
121
+ multi_spinner = TTY::Spinner::Multi.new(
122
+ "[:spinner] SEEDING MODELS FOR #{label}",
123
+ hide_cursor: true,
124
+ success_mark: pastel.green('✓'),
125
+ error_mark: pastel.red('×'),
126
+ interval: 5,
127
+ format: :arc
128
+ )
129
+
130
+ departments.each do |department|
131
+ next unless seeds_available?(environment, department)
132
+
133
+ seed_class = "Seeds::#{env_class}::#{department.key.classify}".constantize
134
+
135
+ display(department.name.upcase, indent: 1)
136
+ seed_from(seed_class, department: department)
137
+ end
138
+ end
139
+
140
+ def self.require_seed_modules!
141
+ Dir[Rails.root.join('db/seeds/**/*.rb')].sort.each { |s| require(s) }
142
+ end
143
+
144
+ def self.seed_from(seed_class, department:)
145
+ seed_methods(seed_class).each do |seed_method|
146
+ title = seed_method[4..-1].titleize
147
+ display("Seeding #{title}...\n", indent: 2)
148
+ seed_class.public_send(seed_method, department)
149
+ display("Seeded: #{title}\n", indent: 2)
150
+ end
151
+ end
152
+
153
+ def self.seed_methods(seed_class)
154
+ methods = seed_class.methods.map(&:to_s).select do |method|
155
+ method.starts_with?('seed_')
156
+ end
157
+ methods.sort_by { |method| seed_class.method(method).source_location.last }
158
+ end
159
+
160
+ def self.display(text, indent: 0, header: false)
161
+ text = "\n====== #{text}" if header
162
+ text = "#{' ' * indent}- #{text}" if indent > 0
163
+
164
+ puts(text)
165
+ end
166
+
167
+ def self.seeds_available?(environment, department)
168
+ seed_path = "db/seeds/#{environment}/#{department.key}/*.rb"
169
+ Dir[Rails.root.join(seed_path)].count > 0
170
+ end
171
+ end
172
+
173
+ Seeds.sprout
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Boothby
4
+ VERSION = '0.1.1'
5
+ end