rubiclifier 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fd4ba1b9ad523fe07ff82c350cb25fdbc4e888b05d3706acb65939af67fb3fed
4
+ data.tar.gz: b99ec401e3abe0a155c7548babe520dad1385ab64151162237bb92a3080361a6
5
+ SHA512:
6
+ metadata.gz: e543847ffc6a0f3a9de7a6fdc19355bd5a13157f721c3924d1b5e9ac839f8d7e56660e63aca4a21f497b31dc4393fb7ffa0af02ed8692d21c4e8cf497e3e2199
7
+ data.tar.gz: b3b203e291003208172f463cb2d9241fb7b99b5ad2e185513b0002701d1c4466c71bed82497d14671fa7f7b3281e20fea2623e0af4fb0971e55e94a761c5fdae
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "httparty"
6
+ gem "sqlite3"
@@ -0,0 +1,35 @@
1
+ module Rubiclifier
2
+ class Args
3
+ attr_reader :args
4
+ private :args
5
+
6
+ def initialize(args)
7
+ @args = args
8
+ end
9
+
10
+ def command
11
+ args[0]
12
+ end
13
+
14
+ def subcommand
15
+ args[1]
16
+ end
17
+
18
+ def none?
19
+ args.length == 0
20
+ end
21
+
22
+ def string(full_name, aliased = nil)
23
+ pos = args.find_index { |a| a == "--#{full_name}" || a == "-#{aliased}" }
24
+ pos && args[pos + 1]
25
+ end
26
+
27
+ def number(full_name, aliased = nil)
28
+ string(full_name, aliased)&.to_i
29
+ end
30
+
31
+ def boolean(full_name, aliased = nil)
32
+ !!args.find_index { |a| a == "--#{full_name}" || a == "-#{aliased}" }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,52 @@
1
+ require "httparty"
2
+
3
+ module Rubiclifier
4
+ class BaseApi
5
+ include HTTParty
6
+
7
+ def self.api_token
8
+ base_uri(base_api_url)
9
+ @api_token ||= DB.get_setting(api_token_db_key) ||
10
+ begin
11
+ t = login_and_get_api_token
12
+ if t.nil?
13
+ invalid_credentials_error
14
+ else
15
+ DB.save_setting(api_token_db_key, t, is_secret: true)
16
+ t
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.invalid_credentials_error
22
+ raise "Invalid credentials!"
23
+ end
24
+
25
+ def self.login_and_get_api_token
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def self.api_token_db_key
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def self.base_api_url_db_key
34
+ raise NotImplementedError
35
+ end
36
+
37
+ def self.wrap_with_authentication(&block)
38
+ res = block.call
39
+ if res.code == 401 || res.code == 403
40
+ @api_token = nil
41
+ DB.save_setting(api_token_db_key, nil, is_secret: false)
42
+ block.call
43
+ else
44
+ res
45
+ end
46
+ end
47
+
48
+ def self.base_api_url
49
+ @base_api_url ||= DB.get_setting(base_api_url_db_key)&.chomp("/")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,118 @@
1
+ require "io/console"
2
+ require_relative "./args.rb"
3
+ require_relative "./colorized_strings.rb"
4
+ require_relative "./setting.rb"
5
+
6
+ module Rubiclifier
7
+ class BaseApplication
8
+ attr_reader :args
9
+ private :args
10
+
11
+ def initialize(args)
12
+ @args = Args.new(args)
13
+ DB.hydrate(data_directory, migrations_location)
14
+ end
15
+
16
+ def call
17
+ show_help if args.command == "help" || args.boolean("help", "h")
18
+ setup_or_fail
19
+ run_application
20
+ end
21
+
22
+ def show_help
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def run_application
27
+ raise NotImplementedError
28
+ end
29
+
30
+ def additional_setup
31
+ end
32
+
33
+ def not_setup
34
+ end
35
+
36
+ def is_background_service?
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def sends_notifications?
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def settings
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def executable_name
49
+ raise NotImplementedError
50
+ end
51
+
52
+ def data_directory
53
+ raise NotImplementedError
54
+ end
55
+
56
+ def migrations_location
57
+ nil
58
+ end
59
+
60
+ def brew_dependencies
61
+ []
62
+ end
63
+
64
+ def setup_or_fail
65
+ if args.command == "setup" || args.boolean("setup", "s")
66
+ puts("Installing brew dependencies...")
67
+ all_brew_dependencies = [
68
+ "sqlite",
69
+ ("terminal-notifier" if sends_notifications?)
70
+ ].concat(brew_dependencies).compact
71
+ system("brew install #{all_brew_dependencies.join(" ")}")
72
+
73
+ puts
74
+
75
+ settings.each do |s|
76
+ s.populate
77
+ end
78
+
79
+ additional_setup
80
+
81
+ puts
82
+
83
+ if is_background_service?
84
+ setup_as_background_service
85
+ else
86
+ puts("Finished setup! Run with `#{executable_name}`".green)
87
+ end
88
+ exit
89
+ end
90
+
91
+ unless settings.all?(&:is_setup?)
92
+ not_setup
93
+ puts
94
+ puts("Oops! You must finish setup first by running with the `--setup` option.".red)
95
+ puts(" `#{executable_name} --setup`".red)
96
+ exit
97
+ end
98
+ end
99
+
100
+ def setup_as_background_service
101
+ puts("It's recommended that you set this up as a system service with serviceman. You can check it out here: https://git.rootprojects.org/root/serviceman".blue)
102
+ puts("Set #{executable_name} to run on startup with `serviceman add --name #{executable_name} #{executable_name}`".blue)
103
+ puts
104
+ print("Would you like this script to set it up for you? (y/n) ")
105
+ if STDIN.gets.chomp.downcase == "y"
106
+ puts "Installing serviceman..."
107
+ system("curl -sL https://webinstall.dev/serviceman | bash")
108
+ puts "Adding #{executable_name} as a service..."
109
+ system("serviceman add --name #{executable_name} #{executable_name}")
110
+ puts
111
+ puts("Finished setup! The service is set to run in the background!".green)
112
+ else
113
+ puts
114
+ puts("Finished setup!".green)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,29 @@
1
+ require "base64"
2
+ require "openssl"
3
+ require "digest/sha1"
4
+
5
+ class Cipher
6
+ def self.encrypt(text)
7
+ cipher = OpenSSL::Cipher.new("aes-256-cbc")
8
+ cipher.encrypt
9
+ key = Digest::SHA1.hexdigest("yourpass")[0..31]
10
+ iv = cipher.random_iv
11
+ cipher.key = key
12
+ cipher.iv = iv
13
+ encrypted = cipher.update(text)
14
+ encrypted << cipher.final
15
+ ["#{key}:#{Base64.encode64(iv).encode("utf-8")}", Base64.encode64(encrypted).encode("utf-8")]
16
+ end
17
+
18
+ def self.decrypt(encrypted, salt)
19
+ key, iv = salt.split(":")
20
+ iv = Base64.decode64(iv.encode("ascii-8bit"))
21
+ cipher = OpenSSL::Cipher.new("aes-256-cbc")
22
+ cipher.decrypt
23
+ cipher.key = key
24
+ cipher.iv = iv
25
+ decrypted = cipher.update(Base64.decode64(encrypted.encode("ascii-8bit")))
26
+ decrypted << cipher.final
27
+ decrypted
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ class String
2
+ def colorize(color_code)
3
+ "\e[#{color_code}m#{self}\e[0m"
4
+ end
5
+
6
+ def red
7
+ colorize(31)
8
+ end
9
+
10
+ def green
11
+ colorize(32)
12
+ end
13
+
14
+ def yellow
15
+ colorize(33)
16
+ end
17
+
18
+ def blue
19
+ colorize(34)
20
+ end
21
+
22
+ def pink
23
+ colorize(35)
24
+ end
25
+
26
+ def light_blue
27
+ colorize(36)
28
+ end
29
+ end
@@ -0,0 +1,62 @@
1
+ require "sqlite3"
2
+ require_relative "./cipher.rb"
3
+
4
+ module Rubiclifier
5
+ class DB
6
+ SETTINGS_TABLE_MIGRATION = "CREATE TABLE IF NOT EXISTS settings (key VARCHAR(50) PRIMARY KEY, value TEXT, salt TEXT);"
7
+
8
+ def self.hydrate(data_directory, migrations_location)
9
+ system("mkdir -p \"#{data_directory.sub("~", "$HOME")}\"")
10
+ @conn = SQLite3::Database.new(File.expand_path("#{data_directory}/data.sqlite3"))
11
+ migrate_if_needed(migrations_location)
12
+ end
13
+
14
+ def self.conn
15
+ @conn
16
+ end
17
+
18
+ def self.execute(sql)
19
+ conn.execute(sql)
20
+ end
21
+
22
+ def self.query_single_row(sql)
23
+ conn.execute(sql) do |row|
24
+ return row
25
+ end
26
+ return nil
27
+ end
28
+
29
+ def self.get_setting(key)
30
+ row = query_single_row("SELECT value, salt FROM settings WHERE key = '#{key}'")
31
+ if row && row[1]
32
+ Cipher.decrypt(row[0], row[1])
33
+ elsif row
34
+ row[0]
35
+ end
36
+ end
37
+
38
+ def self.save_setting(key, value, is_secret:)
39
+ salt = "NULL"
40
+ if is_secret
41
+ salt, encrypted = Cipher.encrypt(value)
42
+ salt = "'#{salt}'"
43
+ value = encrypted
44
+ end
45
+ conn.execute("DELETE FROM settings WHERE key = '#{key}';")
46
+ conn.execute("INSERT INTO settings (key, value, salt) VALUES('#{key}', '#{value}', #{salt});")
47
+ end
48
+
49
+ def self.migrate_if_needed(migrations_location)
50
+ all_migrations = [SETTINGS_TABLE_MIGRATION]
51
+ all_migrations.concat(eval(File.read(migrations_location))) if migrations_location
52
+ conn.execute("CREATE TABLE IF NOT EXISTS migrations (id INT PRIMARY KEY);")
53
+ last_migration = query_single_row("SELECT id FROM migrations ORDER BY id DESC LIMIT 1;")&.to_a&.fetch(0) || -1
54
+ if all_migrations.length - 1 > last_migration
55
+ all_migrations[(last_migration + 1)..-1].each_with_index do |sql, i|
56
+ conn.execute(sql)
57
+ conn.execute("INSERT INTO migrations (id) VALUES (#{i + last_migration + 1});")
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,30 @@
1
+ module Rubiclifier
2
+ class Notification
3
+ attr_reader :title, :message, :subtitle, :icon, :url
4
+ private :title, :message, :subtitle, :icon, :url
5
+ def initialize(title, message, subtitle = nil, icon = nil, url = nil)
6
+ @title = title
7
+ @message = message
8
+ @subtitle = subtitle
9
+ @icon = icon
10
+ @url = url
11
+ end
12
+
13
+ def send
14
+ args = {
15
+ "title" => title,
16
+ "message" => message,
17
+ "subtitle" => subtitle,
18
+ "appIcon" => icon,
19
+ "open" => url
20
+ }
21
+ all_args = args.keys.reduce("") do |arg_string, key|
22
+ if args[key]
23
+ arg_string += " -#{key} '#{args[key]}'"
24
+ end
25
+ arg_string
26
+ end
27
+ system("/usr/local/bin/terminal-notifier #{all_args}")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "./args.rb"
2
+ require_relative "./base_api.rb"
3
+ require_relative "./base_application.rb"
4
+ require_relative "./cipher.rb"
5
+ require_relative "./colorized_strings.rb"
6
+ require_relative "./database.rb"
7
+ require_relative "./notification.rb"
8
+ require_relative "./setting.rb"
@@ -0,0 +1,39 @@
1
+ module Rubiclifier
2
+ class Setting
3
+ attr_reader :key, :label, :explanation, :is_secret, :nullable
4
+ private :key, :label, :explanation, :is_secret, :nullable
5
+
6
+ def initialize(key, label, explanation: nil, is_secret: false, nullable: false)
7
+ @key = key
8
+ @label = label
9
+ @explanation = explanation
10
+ @is_secret = is_secret
11
+ @nullable = nullable
12
+ end
13
+
14
+ def populate
15
+ print("What's the #{label}? #{explanation_text}")
16
+ input = if is_secret
17
+ STDIN.noecho(&:gets).chomp.tap { puts }
18
+ else
19
+ STDIN.gets.chomp
20
+ end
21
+ if input == ""
22
+ input = nil
23
+ end
24
+ DB.save_setting(key, input, is_secret: is_secret) unless input.nil?
25
+ end
26
+
27
+ def explanation_text
28
+ if explanation.is_a?(Proc)
29
+ "(#{explanation.call}) "
30
+ elsif explanation
31
+ "(#{explanation}) "
32
+ end
33
+ end
34
+
35
+ def is_setup?
36
+ DB.get_setting(key) || nullable
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubiclifier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kyle Grinstead
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: kyleag@hey.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - Gemfile
20
+ - lib/args.rb
21
+ - lib/base_api.rb
22
+ - lib/base_application.rb
23
+ - lib/cipher.rb
24
+ - lib/colorized_strings.rb
25
+ - lib/database.rb
26
+ - lib/notification.rb
27
+ - lib/rubiclifier.rb
28
+ - lib/setting.rb
29
+ homepage: https://rubygems.org/gems/rubiclifier
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ source_code_uri: https://github.com/MrGrinst/rubiclifier
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 2.7.6.2
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Easily spin up new Ruby CLI applications with built-in backgrounding and
54
+ data storage.
55
+ test_files: []