Skip to content

Instantly share code, notes, and snippets.

@CharlyJazz
Last active June 26, 2022 21:47
Show Gist options
  • Save CharlyJazz/7b9c4d18f60ffe8022526a936366e171 to your computer and use it in GitHub Desktop.
Save CharlyJazz/7b9c4d18f60ffe8022526a936366e171 to your computer and use it in GitHub Desktop.
upload files to gist using ruby
# Struct to save file data
FileStruct = Struct.new(:filename, :content)
# Resolver content from a file of a folder of files
class ContentResolver
# Returns whether or not +file+ is a binary file. Note that this is
# not guaranteed to be 100% accurate. It performs a "best guess" based
# on a simple test of the first +File.blksize+ characters.
#
# Example:
#
# File.binary?('somefile.exe') # => true
# File.binary?('somefile.txt') # => false
#--
# Based on code originally provided by Ryan Davis (which, in turn, is
# based on Perl's -B switch).
#
# https://makandracards.com/makandra/483046-how-to-check-if-a-file-is-a-human-readable-text-file
def binary?(file)
s = (File.read(file, File.stat(file).blksize) || '').split(//)
((s.size - s.grep(' '..'~').size) / s.size.to_f) > 0.30
end
# Recursively get the content and the path of text files.
# If the path is a folder then create a recursive call
# but keeping the reference of list_of_files
def get_files_from_folder(initial_path, list_of_files = [])
Dir.glob("#{initial_path}/*").each do |filename|
if Dir.exist? filename
get_files_from_folder(filename, list_of_files)
else
file_content = File.read(filename)
list_of_files.push FileStruct.new(filename, file_content) unless binary?(filename)
end
end
list_of_files
end
end
require 'uri'
require 'json'
require_relative './content_resolver'
require_relative './http-client'
# Error for credentials related issues
class GistValidationError < RuntimeError
end
## Error for gist creation issues
class GistCreationError < RuntimeError
end
# A class to provide the logic to create gist in you github account
# using you username and a access_token
class Gist
attr_accessor :username, :token, :public, :description, :list_of_files, :base_url
def initialize(http_client = HTTPProvider)
@resolver = ContentResolver.new
@list_of_files = []
@base_url = 'https://api.github.com'
@http_client = http_client.new
end
# Call all the functions to create the gist
def send_new_gist
input_credentials
verify_access_token
input_description_and_public
input_file_content
validate_response send_request_to_create_gist
end
private
# Create request, send it and validate status
def send_request_to_create_gist
uri = URI("#{@base_url}/gists")
http = @http_client.http.new(uri.host, uri.port)
http.use_ssl = true
request = @http_client.http_post.new(uri, header)
files = {}
@list_of_files.each do |file_struct|
# To prevent send a empty content and raise a error
next unless file_struct[:content].length > 0
content_sanitized = file_struct[:content]
filename_sanitized = file_struct[:filename].delete('/')
files[filename_sanitized] = { "content": content_sanitized }
end
request.body = {
"files": files,
"description": @description,
"public": @public
}.to_json
http.request(request)
end
# Helper to get the header to create gist
def header
{
'Content-Type' => 'application/json',
'Authorization' => "token #{@token}",
'Accept' => 'application/vnd.github.v3+json',
'User-Agent' => @username
}
end
# Validate the create gist response
def validate_response(response)
case response
when @http_client.http_success
puts 'Gist created with success'
puts "Visit: #{JSON.parse(response.body)['html_url']}"
when @http_client.http_redirect
puts 'Request redirected, something went wrong'
else
puts 'Something went wrong'
end
end
# Show message and raise error
def wrong_credentials_scenario
puts 'Credentials wrong try again'
raise GistValidationError
end
# Save data about the gist
def input_description_and_public
puts 'Insert description for the gist'
@description = gets.strip
puts 'Do you want a public gist? [y/n]'
@public = gets.strip
@public = @public == 'y'
end
# Save data about the credentials
def input_credentials
puts 'Insert username'
@username = gets.strip
puts 'Insert the access token'
@token = gets.strip
end
# Verify access token using a ruby version of the
# curl command from the github documentation
# curl -u username:token https://api.github.com/user
def verify_access_token
puts 'Verifing access token'
uri = URI("#{@base_url}/user")
http = @http_client.http.new(uri.host, uri.port)
http.use_ssl = true
request = @http_client.http_get.new(uri)
request.basic_auth(@username, @token)
response = http.request(request)
wrong_credentials_scenario if response.code != '200'
end
# Check if the user enter a folder and then get all the files or just
# get a single file
def input_file_content
puts 'Insert file name'
path = gets.strip
begin
if Dir.exist? path
@resolver.get_files_from_folder(path, @list_of_files)
else
@list_of_files.push(FileStruct.new(path, File.read(path)))
end
rescue Errno::ENOENT => e
wrong_file_scenario
end
end
# Show message and raise error
def wrong_file_scenario
puts 'File no founded'
raise GistCreationError
end
end
require 'net/https'
# Class to provide the HTTP Protocol.
#
# This class is useful because can be mocked for testing
class HTTPProvider
# Return Net::HTTP
def http
Net::HTTP
end
# Return Net::HTTP::Post
def http_post
Net::HTTP::Post
end
# Return Net::HTTP::Get
def http_get
Net::HTTP::Get
end
# Return Net::HTTPSuccess
def http_success
Net::HTTPSuccess
end
# Return Net::HTTPRedirection
def http_redirect
Net::HTTPRedirection
end
end
# Class for mock the Net::HTTPSuccess instance
class MockHTTPSuccess
attr_accessor :body
def initialize(body)
@body = body
end
end
# Class for mock the Net::HTTPRedirection instance
class MockRedirectSuccess end
# Simple Stubs of HTTPProvider
class HTTPProviderMock
# Do nothing (Stub)
def http; end
# Do nothing (Stub)
def http_post; end
# Do nothing (Stub)
def http_get; end
# Return MockRedirectSuccess
def http_success
MockRedirectSuccess
end
# Return MockRedirectSuccess
def http_redirect
MockRedirectSuccess
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment