bcli 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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +9 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +7 -0
  5. data/.standard.yml +3 -0
  6. data/CHANGELOG.md +5 -0
  7. data/CODE_OF_CONDUCT.md +84 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +120 -0
  10. data/LICENSE.txt +20 -0
  11. data/README.md +39 -0
  12. data/Rakefile +10 -0
  13. data/bcli.gemspec +116 -0
  14. data/exe/bcli +18 -0
  15. data/lib/bcli/cli.rb +54 -0
  16. data/lib/bcli/client.rb +42 -0
  17. data/lib/bcli/command.rb +101 -0
  18. data/lib/bcli/commands/.gitkeep +1 -0
  19. data/lib/bcli/commands/bookmarks/create.rb +52 -0
  20. data/lib/bcli/commands/bookmarks/search.rb +45 -0
  21. data/lib/bcli/commands/bookmarks.rb +35 -0
  22. data/lib/bcli/commands/drive/search.rb +61 -0
  23. data/lib/bcli/commands/drive/upload.rb +34 -0
  24. data/lib/bcli/commands/drive.rb +35 -0
  25. data/lib/bcli/commands/login.rb +62 -0
  26. data/lib/bcli/commands/news.rb +42 -0
  27. data/lib/bcli/commands/snippets/create.rb +47 -0
  28. data/lib/bcli/commands/snippets/search.rb +56 -0
  29. data/lib/bcli/commands/snippets.rb +35 -0
  30. data/lib/bcli/config.rb +24 -0
  31. data/lib/bcli/file_client.rb +32 -0
  32. data/lib/bcli/templates/.gitkeep +1 -0
  33. data/lib/bcli/templates/bookmarks/create/.gitkeep +0 -0
  34. data/lib/bcli/templates/bookmarks/search/.gitkeep +0 -0
  35. data/lib/bcli/templates/drive/search/.gitkeep +0 -0
  36. data/lib/bcli/templates/drive/upload/.gitkeep +0 -0
  37. data/lib/bcli/templates/login/.gitkeep +0 -0
  38. data/lib/bcli/templates/news/.gitkeep +0 -0
  39. data/lib/bcli/templates/snippets/create/.gitkeep +0 -0
  40. data/lib/bcli/templates/snippets/search/.gitkeep +0 -0
  41. data/lib/bcli/version.rb +5 -0
  42. data/lib/bcli.rb +17 -0
  43. data/sig/bcli.rbs +4 -0
  44. metadata +257 -0
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require_relative "../../../bcli"
5
+
6
+ module Bcli
7
+ module Commands
8
+ class Bookmarks
9
+ class Create < Bcli::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 "../../../bcli"
4
+
5
+ module Bcli
6
+ module Commands
7
+ class Bookmarks
8
+ class Search < Bcli::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 Bcli
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
+ Bcli::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
+ Bcli::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 "../../../bcli"
4
+
5
+ module Bcli
6
+ module Commands
7
+ class Drive
8
+ class Search < Bcli::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 "../../../bcli"
4
+
5
+ module Bcli
6
+ module Commands
7
+ class Drive
8
+ class Upload < Bcli::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 ||= Bcli::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 Bcli
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
+ Bcli::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
+ Bcli::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 "../../bcli"
4
+
5
+ module Bcli
6
+ module Commands
7
+ class Login < Bcli::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("#{Bcli::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 = Bcli::Config.new
50
+ puts pastel.green "Updated: #{file_path}"
51
+ end
52
+
53
+ def base
54
+ @base ||= Bcli::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 "../../bcli"
4
+
5
+ module Bcli
6
+ module Commands
7
+ class News < Bcli::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 "../../../bcli"
4
+
5
+ module Bcli
6
+ module Commands
7
+ class Snippets
8
+ class Create < Bcli::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,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../bcli"
4
+
5
+ module Bcli
6
+ module Commands
7
+ class Snippets
8
+ class Search < Bcli::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
+ Clipboard.copy(snippet["code"])
37
+ puts pastel.green("Snippet copied to clipboard.")
38
+
39
+ pastel.white(link_to(snippet["name"], pastel.blue(snippet["url"])))
40
+ end
41
+ end
42
+
43
+ def choices(response, headers)
44
+ choices = response.map { |snippet| snippet["name"] }
45
+
46
+ choices << "Next Page" if headers["Total-Pages"].to_i > 1
47
+ choices
48
+ end
49
+
50
+ def query
51
+ ask("Enter your search query:")
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+
5
+ module Bcli
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
+ Bcli::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
+ Bcli::Commands::Snippets::Create.new(options).execute
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ class Bcli::Config
2
+ attr_reader :creds, :file_path
3
+
4
+ def initialize
5
+ @file_path = File.expand_path("~/.bcli.yml")
6
+ @creds = read_from_file
7
+ if token_expired?
8
+ puts Bcli::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 Bcli::FileClient < Bcli::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 = Bcli::Config.new
7
+ @options = options
8
+ end
9
+
10
+ def upload
11
+ HTTParty.post("#{Bcli::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
File without changes
File without changes
File without changes
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bcli
4
+ VERSION = "0.1.0"
5
+ end
data/lib/bcli.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httparty"
4
+ require "byebug"
5
+ require "yaml"
6
+ require "clipboard"
7
+
8
+ require_relative "bcli/version"
9
+ require_relative "bcli/command"
10
+ require_relative "bcli/config"
11
+ require_relative "bcli/client"
12
+ require_relative "bcli/file_client"
13
+
14
+ module Bcli
15
+ class Error < StandardError; end
16
+ # Your code goes here...
17
+ end
data/sig/bcli.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Bcli
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end