boilercode 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.
- checksums.yaml +7 -0
- data/.editorconfig +9 -0
- data/.rspec +3 -0
- data/.rubocop.yml +7 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +137 -0
- data/LICENSE.txt +20 -0
- data/README.md +39 -0
- data/Rakefile +10 -0
- data/boilercode.gemspec +116 -0
- data/exe/boilercode +18 -0
- data/lib/boilercode/cli.rb +54 -0
- data/lib/boilercode/client.rb +42 -0
- data/lib/boilercode/command.rb +106 -0
- data/lib/boilercode/commands/.gitkeep +1 -0
- data/lib/boilercode/commands/bookmarks/create.rb +52 -0
- data/lib/boilercode/commands/bookmarks/search.rb +45 -0
- data/lib/boilercode/commands/bookmarks.rb +35 -0
- data/lib/boilercode/commands/drive/search.rb +61 -0
- data/lib/boilercode/commands/drive/upload.rb +34 -0
- data/lib/boilercode/commands/drive.rb +35 -0
- data/lib/boilercode/commands/login.rb +62 -0
- data/lib/boilercode/commands/news.rb +42 -0
- data/lib/boilercode/commands/snippets/create.rb +47 -0
- data/lib/boilercode/commands/snippets/search.rb +60 -0
- data/lib/boilercode/commands/snippets.rb +35 -0
- data/lib/boilercode/config.rb +24 -0
- data/lib/boilercode/file_client.rb +32 -0
- data/lib/boilercode/templates/.gitkeep +1 -0
- data/lib/boilercode/templates/bookmarks/create/.gitkeep +0 -0
- data/lib/boilercode/templates/bookmarks/search/.gitkeep +0 -0
- data/lib/boilercode/templates/drive/search/.gitkeep +0 -0
- data/lib/boilercode/templates/drive/upload/.gitkeep +0 -0
- data/lib/boilercode/templates/login/.gitkeep +0 -0
- data/lib/boilercode/templates/news/.gitkeep +0 -0
- data/lib/boilercode/templates/snippets/create/.gitkeep +0 -0
- data/lib/boilercode/templates/snippets/search/.gitkeep +0 -0
- data/lib/boilercode/version.rb +5 -0
- data/lib/boilercode.rb +16 -0
- data/rename.rb +1 -0
- data/sig/boilercode.rbs +4 -0
- metadata +272 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Boilercode
|
|
4
|
+
class Command
|
|
5
|
+
# Execute this command
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
def execute(*)
|
|
9
|
+
raise(
|
|
10
|
+
NotImplementedError,
|
|
11
|
+
"#{self.class}##{__method__} must be implemented"
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Below are examples of how to integrate TTY components
|
|
16
|
+
|
|
17
|
+
# The external commands runner
|
|
18
|
+
#
|
|
19
|
+
# @see http://www.rubydoc.info/gems/tty-command
|
|
20
|
+
#
|
|
21
|
+
# @api public
|
|
22
|
+
# def command(**options)
|
|
23
|
+
# require "tty-command"
|
|
24
|
+
# TTY::Command.new(options)
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
# The interactive prompt
|
|
28
|
+
#
|
|
29
|
+
# @see http://www.rubydoc.info/gems/tty-prompt
|
|
30
|
+
#
|
|
31
|
+
# @api public
|
|
32
|
+
def prompt
|
|
33
|
+
require "tty-prompt"
|
|
34
|
+
TTY::Prompt.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @api public
|
|
38
|
+
def link_to(url, title = nil)
|
|
39
|
+
require "tty-link"
|
|
40
|
+
|
|
41
|
+
TTY::Link.link_to(url, title)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def logger
|
|
45
|
+
require "tty-logger"
|
|
46
|
+
TTY::Logger.new
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @api public
|
|
50
|
+
def download(url)
|
|
51
|
+
require "open-uri"
|
|
52
|
+
filename = File.basename(url)
|
|
53
|
+
# rubocop:disable Security/Open
|
|
54
|
+
remote_file = URI.open(url)
|
|
55
|
+
# rubocop:enable Security/Open
|
|
56
|
+
File.write(filename, remote_file.read)
|
|
57
|
+
remote_file.close
|
|
58
|
+
file_path = File.expand_path(filename)
|
|
59
|
+
puts pastel.green "Downloaded #{filename} to #{file_path}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @api public
|
|
63
|
+
def pastel
|
|
64
|
+
require "pastel"
|
|
65
|
+
@pastel ||= Pastel.new
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @api public
|
|
69
|
+
def config
|
|
70
|
+
# require "tty-config"
|
|
71
|
+
@config ||= Boilercode::Config.new
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @api public
|
|
75
|
+
def credentials
|
|
76
|
+
config.creds
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @api public
|
|
80
|
+
def client
|
|
81
|
+
@client ||= Boilercode::Client.new
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @api public
|
|
85
|
+
def open_in_browser(url)
|
|
86
|
+
system("open", url)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @api public
|
|
90
|
+
def parse_json(response)
|
|
91
|
+
JSON.parse(response.body, object_class: OpenStruct)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def markdown(text)
|
|
95
|
+
require "tty-markdown"
|
|
96
|
+
puts TTY::Markdown.parse(text)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @api public
|
|
100
|
+
def ask(question)
|
|
101
|
+
prompt.ask(question) do |q|
|
|
102
|
+
q.required true
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
require_relative "../../../boilercode"
|
|
5
|
+
|
|
6
|
+
module Boilercode
|
|
7
|
+
module Commands
|
|
8
|
+
class Bookmarks
|
|
9
|
+
class Create < Boilercode::Command
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
@check_valid_url = proc { |url|
|
|
13
|
+
uri_regex = URI::DEFAULT_PARSER.make_regexp(%w[http https])
|
|
14
|
+
!!(url =~ uri_regex)
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def execute(input: $stdin, output: $stdout)
|
|
19
|
+
res = client.post("/bookmarks", payload)
|
|
20
|
+
if res.code == 201
|
|
21
|
+
response = parse_json(res)
|
|
22
|
+
output.puts pastel.green response.url
|
|
23
|
+
else
|
|
24
|
+
output.puts pastel.red res.body
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def title
|
|
31
|
+
prompt.ask("Enter a bookmark title (Optional)")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def url
|
|
35
|
+
prompt.ask("Provide a URL to bookmark") do |q|
|
|
36
|
+
q.required true
|
|
37
|
+
q.validate proc { |s| valid_url?(s) }, "Not valid"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def valid_url?(url)
|
|
42
|
+
uri_regex = URI::DEFAULT_PARSER.make_regexp(%w[http https])
|
|
43
|
+
!!(url =~ uri_regex)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def payload
|
|
47
|
+
{bookmark: {url: url}}
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../boilercode"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Bookmarks
|
|
8
|
+
class Search < Boilercode::Command
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute(input: $stdin, output: $stdout)
|
|
14
|
+
res = client.get("/bookmarks?q[title_or_url_cont]=#{query}")
|
|
15
|
+
if res.success?
|
|
16
|
+
output.puts handle_search_response(parse_json(res))
|
|
17
|
+
else
|
|
18
|
+
output.puts pastel.red res.inspect
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def handle_search_response(response)
|
|
25
|
+
if response.empty?
|
|
26
|
+
pastel.red "No bookmarks found, try updating your query."
|
|
27
|
+
else
|
|
28
|
+
choice = prompt.select("Choose your bookmark:", response.map { |bookmark| bookmark["title"] })
|
|
29
|
+
bookmark = response.find do |bm|
|
|
30
|
+
bm.title == choice
|
|
31
|
+
end
|
|
32
|
+
puts pastel.green(link_to(bookmark["title"], bookmark["url"]))
|
|
33
|
+
Clipboard.copy(bookmark["short_url"])
|
|
34
|
+
open_in_browser(bookmark["url"])
|
|
35
|
+
pastel.green("Bookmark copied to clipboard.")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def query
|
|
40
|
+
ask("Search for a bookmark:")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Bookmarks < Thor
|
|
8
|
+
namespace :bookmarks
|
|
9
|
+
|
|
10
|
+
desc "search QUERY", "Search through your bookmarks and add selected bookmark to clipboard"
|
|
11
|
+
method_option :help, aliases: "-h", type: :boolean,
|
|
12
|
+
desc: "Display usage information"
|
|
13
|
+
def search
|
|
14
|
+
if options[:help]
|
|
15
|
+
invoke :help, ["search"]
|
|
16
|
+
else
|
|
17
|
+
require_relative "bookmarks/search"
|
|
18
|
+
Boilercode::Commands::Bookmarks::Search.new(options).execute
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "create", "Takes URL from clipboard and saves it to your BoilerCode bookmarks"
|
|
23
|
+
method_option :help, aliases: "-h", type: :boolean,
|
|
24
|
+
desc: "Display usage information"
|
|
25
|
+
def create(*)
|
|
26
|
+
if options[:help]
|
|
27
|
+
invoke :help, ["create"]
|
|
28
|
+
else
|
|
29
|
+
require_relative "bookmarks/create"
|
|
30
|
+
Boilercode::Commands::Bookmarks::Create.new(options).execute
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../boilercode"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Drive
|
|
8
|
+
class Search < Boilercode::Command
|
|
9
|
+
# attr_reader :query, :download_file
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
# @download_file = options[:download]
|
|
13
|
+
# @query = options[:query]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def execute(input: $stdin, output: $stdout, page: 1)
|
|
17
|
+
res = client.get("/uploads?q[filename_cont]=#{query}&page=#{page}")
|
|
18
|
+
if res.success?
|
|
19
|
+
output.puts handle_search_response(res)
|
|
20
|
+
else
|
|
21
|
+
output.puts pastel.red res.inspect
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def handle_search_response(res)
|
|
28
|
+
headers = res.headers
|
|
29
|
+
response = parse_json(res)
|
|
30
|
+
if response.empty?
|
|
31
|
+
pastel.red "No uploads found, try updating your query."
|
|
32
|
+
else
|
|
33
|
+
choice = prompt.select("Choose your upload:", choices(response, headers))
|
|
34
|
+
if choice == "Next Page"
|
|
35
|
+
return execute(page: headers["Current-Page"].to_i + 1)
|
|
36
|
+
end
|
|
37
|
+
upload = response.find { |upload| upload["filename"] == choice }
|
|
38
|
+
|
|
39
|
+
download(upload["url"]) if download_file?
|
|
40
|
+
pastel.green(upload["url"])
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def choices(response, headers)
|
|
45
|
+
choices = response.map { |upload| upload["filename"] }
|
|
46
|
+
|
|
47
|
+
choices << "Next Page" if headers["Total-Pages"].to_i > 1
|
|
48
|
+
choices
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def download_file?
|
|
52
|
+
ask("Do you want to download the file? (y/n)") == "y"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def query
|
|
56
|
+
ask("Enter your search query:")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../boilercode"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Drive
|
|
8
|
+
class Upload < Boilercode::Command
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute(input: $stdin, output: $stdout)
|
|
14
|
+
res = file_client.upload
|
|
15
|
+
if res.code == 201
|
|
16
|
+
response = parse_json(res)
|
|
17
|
+
puts pastel.green "Upload successful!\n"
|
|
18
|
+
puts pastel.green response.url
|
|
19
|
+
else
|
|
20
|
+
puts pastel.red "Upload failed!"
|
|
21
|
+
|
|
22
|
+
puts pastel.red res.body
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def file_client
|
|
27
|
+
local_file_path = ask("Enter the path to the file you want to upload: ")
|
|
28
|
+
remote_folder_path = ask("Enter the path to the folder you want to upload to: (optional)")
|
|
29
|
+
@file_client ||= Boilercode::FileClient.new(local_file_path, folder_path: remote_folder_path)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Drive < Thor
|
|
8
|
+
namespace :drive
|
|
9
|
+
|
|
10
|
+
desc "search", "Search for a file, receive the url and optionally download it"
|
|
11
|
+
method_option :help, aliases: "-h", type: :boolean,
|
|
12
|
+
desc: "Display usage information"
|
|
13
|
+
def search
|
|
14
|
+
if options[:help]
|
|
15
|
+
invoke :help, ["search"]
|
|
16
|
+
else
|
|
17
|
+
require_relative "drive/search"
|
|
18
|
+
Boilercode::Commands::Drive::Search.new(options).execute
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "upload", "Upload given file to BoilerCode Drive"
|
|
23
|
+
method_option :help, aliases: "-h", type: :boolean,
|
|
24
|
+
desc: "Upload given file to BoilerCode Drive"
|
|
25
|
+
def upload
|
|
26
|
+
if options[:help]
|
|
27
|
+
invoke :help, ["upload"]
|
|
28
|
+
else
|
|
29
|
+
require_relative "drive/upload"
|
|
30
|
+
Boilercode::Commands::Drive::Upload.new(options).execute
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../boilercode"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Login < Boilercode::Command
|
|
8
|
+
def execute(input: $stdin, output: $stdout)
|
|
9
|
+
set_email_and_password
|
|
10
|
+
write_to_config_file
|
|
11
|
+
output.puts pastel.green "Authenticated"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def set_email_and_password
|
|
17
|
+
if File.exist?(file_path) && credentials[:token]
|
|
18
|
+
@api_key = credentials[:token]
|
|
19
|
+
else
|
|
20
|
+
prompt_for_api_key
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def fetch_bearer_token
|
|
25
|
+
response = HTTParty.post("#{Boilercode::Client.base_uri}/auth", body: payload)
|
|
26
|
+
if response.success?
|
|
27
|
+
@token = JSON.parse(response.body)["token"]
|
|
28
|
+
else
|
|
29
|
+
puts pastel.red "Authentication failed. Please check your credentials."
|
|
30
|
+
end
|
|
31
|
+
@token
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def prompt_for_api_key
|
|
35
|
+
puts pastel.green "Please enter API key:"
|
|
36
|
+
@api_key = $stdin.gets.chomp
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def payload
|
|
40
|
+
{
|
|
41
|
+
user: {email: @email, password: @password}
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def write_to_config_file
|
|
46
|
+
File.write(file_path, {
|
|
47
|
+
token: @api_key
|
|
48
|
+
}.to_yaml)
|
|
49
|
+
@config = Boilercode::Config.new
|
|
50
|
+
puts pastel.green "Updated: #{file_path}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def base
|
|
54
|
+
@base ||= Boilercode::Base.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def file_path
|
|
58
|
+
config.file_path
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../boilercode"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class News < Boilercode::Command
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def execute(input: $stdin, output: $stdout)
|
|
13
|
+
res = client.get("/news")
|
|
14
|
+
if res.success?
|
|
15
|
+
handle_search_response(JSON.parse(res.body.to_s))
|
|
16
|
+
else
|
|
17
|
+
puts pastel.red "Something went wrong."
|
|
18
|
+
puts pastel.red res.body
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def handle_search_response(response)
|
|
25
|
+
choice = prompt.select("Choose your article:", articles(response))
|
|
26
|
+
|
|
27
|
+
puts pastel.green(article(response, choice)["url"])
|
|
28
|
+
Clipboard.copy(article(response, choice)["url"])
|
|
29
|
+
puts pastel.green("article url copied to clipboard.")
|
|
30
|
+
open_in_browser(article(response, choice)["url"])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def articles(response)
|
|
34
|
+
@articles ||= response.map { |article| article["name"] }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def article(response, choice)
|
|
38
|
+
@article ||= response.find { |article| article["name"] == choice }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../boilercode"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Snippets
|
|
8
|
+
class Create < Boilercode::Command
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute(input: $stdin, output: $stdout)
|
|
14
|
+
res = client.post("/snippets", payload(@options))
|
|
15
|
+
if res.code == 201
|
|
16
|
+
response = parse_json(res)
|
|
17
|
+
output.puts pastel.blue(link_to(response.name, response_url(response.url)))
|
|
18
|
+
else
|
|
19
|
+
output.puts pastel.red res
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def response_url(url)
|
|
24
|
+
url.gsub(".json", "")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def payload(options)
|
|
28
|
+
{
|
|
29
|
+
snippet: {
|
|
30
|
+
code: Clipboard.paste,
|
|
31
|
+
language:,
|
|
32
|
+
name:
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def language
|
|
38
|
+
ask("Enter the language of the snippet:")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def name
|
|
42
|
+
ask("Enter the name of the snippet:")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../../boilercode"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Snippets
|
|
8
|
+
class Search < Boilercode::Command
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute(input: $stdin, output: $stdout, page: 1)
|
|
14
|
+
res = client.get("/snippets?q[name_or_code_cont]=#{query}&page=#{page}")
|
|
15
|
+
|
|
16
|
+
if res.success?
|
|
17
|
+
output.puts handle_search_response(res)
|
|
18
|
+
else
|
|
19
|
+
output.puts pastel.red res.inspect
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def handle_search_response(res)
|
|
26
|
+
headers = res.headers
|
|
27
|
+
response = parse_json(res)
|
|
28
|
+
if response.empty?
|
|
29
|
+
pastel.red "No snippets found, try updating your query."
|
|
30
|
+
else
|
|
31
|
+
choice = prompt.select("Choose your snippet:", choices(response, headers))
|
|
32
|
+
if choice == "Next Page"
|
|
33
|
+
return execute(page: headers["Current-Page"].to_i + 1)
|
|
34
|
+
end
|
|
35
|
+
snippet = response.find { |snippet| snippet["name"] == choice }
|
|
36
|
+
language = snippet["language"]
|
|
37
|
+
|
|
38
|
+
Clipboard.copy(snippet["code"])
|
|
39
|
+
puts pastel.green("Snippet copied to clipboard.")
|
|
40
|
+
|
|
41
|
+
puts markdown("```#{language}\n\n#{snippet["code"]}\n\n```")
|
|
42
|
+
|
|
43
|
+
pastel.white(link_to(snippet["name"], pastel.blue(snippet["url"])))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def choices(response, headers)
|
|
48
|
+
choices = response.map { |snippet| snippet["name"] }
|
|
49
|
+
|
|
50
|
+
choices << "Next Page" if headers["Total-Pages"].to_i > 1
|
|
51
|
+
choices
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def query
|
|
55
|
+
ask("Enter your search query:")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module Boilercode
|
|
6
|
+
module Commands
|
|
7
|
+
class Snippets < Thor
|
|
8
|
+
namespace :snippets
|
|
9
|
+
|
|
10
|
+
desc "search", "Search for a snippet and attach it to clipboard"
|
|
11
|
+
method_option :help, aliases: "-h", type: :boolean,
|
|
12
|
+
desc: "Display usage information"
|
|
13
|
+
def search(*)
|
|
14
|
+
if options[:help]
|
|
15
|
+
invoke :help, ["search"]
|
|
16
|
+
else
|
|
17
|
+
require_relative "snippets/search"
|
|
18
|
+
Boilercode::Commands::Snippets::Search.new(options).execute
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "create", "Creates a snippet"
|
|
23
|
+
method_option :help, aliases: "-h", type: :boolean,
|
|
24
|
+
desc: "Display usage information"
|
|
25
|
+
def create(*)
|
|
26
|
+
if options[:help]
|
|
27
|
+
invoke :help, ["create"]
|
|
28
|
+
else
|
|
29
|
+
require_relative "snippets/create"
|
|
30
|
+
Boilercode::Commands::Snippets::Create.new(options).execute
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class Boilercode::Config
|
|
2
|
+
attr_reader :creds, :file_path
|
|
3
|
+
|
|
4
|
+
def initialize
|
|
5
|
+
@file_path = File.expand_path("~/.boilercode.yml")
|
|
6
|
+
@creds = read_from_file
|
|
7
|
+
if token_expired?
|
|
8
|
+
puts Boilercode::Command.new.pastel.red "Token expired. Please re-authenticate."
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def read_from_file
|
|
13
|
+
return {} unless File.exist?(file_path)
|
|
14
|
+
YAML.load_file(file_path)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def token_expired?
|
|
18
|
+
creds[:issued_at] && (Time.now.to_i - creds[:issued_at]) > (3600 * 24)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def creds_present?
|
|
22
|
+
creds[:email] && creds[:password]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class Boilercode::FileClient < Boilercode::Client
|
|
2
|
+
attr_reader :file_path, :content_type, :file, :token, :config
|
|
3
|
+
def initialize(file_path = nil, options = {})
|
|
4
|
+
@file_path = file_path
|
|
5
|
+
@file = file_contents
|
|
6
|
+
@config = Boilercode::Config.new
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def upload
|
|
11
|
+
HTTParty.post("#{Boilercode::Client.base_uri}/uploads",
|
|
12
|
+
method: :post,
|
|
13
|
+
headers: file_headers,
|
|
14
|
+
body: {
|
|
15
|
+
upload: {
|
|
16
|
+
file: file_contents,
|
|
17
|
+
folder_path: @options[:folder_path]
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def file_headers
|
|
23
|
+
{
|
|
24
|
+
"Content-Type": "multipart/form-data",
|
|
25
|
+
Authorization: "Bearer #{config.creds[:token]}"
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def file_contents
|
|
30
|
+
File.open(file_path, "r") if file_path
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|