gurl 1.0.0.pre.alpha

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1a768efa1cf346a124d593c8fd1351fd142187ac6aff5b7dff9d9ca8c3df6718
4
+ data.tar.gz: 99be94f373b5a07681cabfcdd5a160258407f6584bcdf2033f712b847728582e
5
+ SHA512:
6
+ metadata.gz: d38e2df77a70083e914dbb1e34b9a9b4a0fa03fac84808668851ba44d395411df25c09e7671d9c05e871256ebcae17300d3d945a2842498e51a7dd83f028dd1a
7
+ data.tar.gz: df21e6ef1db8de5d126a938b683b501a70ee6bb73938b6bc2b056e7211b979b9edfa06657446df6630996b7d07a42a6c508c832f8ac358e7cf922d41ac4e5df9
data/bin/gurl ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "#{__dir__}/../lib/gurl.rb"
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ GET = 'GET'
4
+ POST = 'POST'
5
+ PUT = 'PUT'
6
+ PATCH = 'PATCH'
7
+ DELETE = 'DELETE'
8
+
9
+ module Builder
10
+ class Base
11
+ include BaseSupport::FlagConcern
12
+
13
+ def initialize
14
+ @url = ''
15
+ @method = GET
16
+ @headers = []
17
+ @data = nil
18
+ @params = []
19
+
20
+ self.class.ancestors.reverse_each do |ancestor|
21
+ next unless ancestor.instance_methods(false).include?(:start)
22
+
23
+ ancestor.instance_method(:start).bind_call(self)
24
+ end
25
+ end
26
+
27
+ def build(**options)
28
+ execute
29
+ one_line = options[:one_line]
30
+
31
+ parts = ['curl']
32
+ parts << _build_url
33
+ parts << "-X #{@method}" if @method != GET
34
+ @headers.each do |k, v|
35
+ parts << "-H '#{k}: #{v}'"
36
+ end
37
+ parts << data_to_curl_argument(one_line) if @data
38
+ parts.join(' ')
39
+ end
40
+
41
+ def execute; end
42
+
43
+ private
44
+
45
+ def data_to_curl_argument(one_line)
46
+ return "-d @- << EOF\n #{@data}\nEOF" unless one_line || !@data.include?("\n")
47
+
48
+ "-d '#{@data.split("\n").map(&:strip).join(' ')}'"
49
+ end
50
+
51
+ def url(url_argument)
52
+ @url = url_argument
53
+ end
54
+
55
+ def method(method_argument)
56
+ @method = method_argument
57
+ end
58
+
59
+ def header(key, value)
60
+ @headers << [key, value]
61
+ end
62
+
63
+ def query_param(key, value)
64
+ @params << [key, value]
65
+ end
66
+
67
+ def body(body_argument)
68
+ @data = body_argument
69
+ end
70
+
71
+ def json_body(json_body_argument)
72
+ require 'json'
73
+ @data = JSON.pretty_generate(json_body_argument)
74
+ end
75
+
76
+ def _build_url
77
+ return @url unless @params.any?
78
+
79
+ q = @params.map { |k, v| "#{k}=#{v}" }.join('&')
80
+ "#{@url}?#{q}"
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Builder
4
+ module BaseSupport
5
+ module FlagConcern
6
+ def process_flags(opts)
7
+ _flags.each do |names, block|
8
+ opts.on(*names, &block)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def flag(name, *list_options, **options)
15
+ return _flag_variables[name] if _flag_variables.key?(name)
16
+
17
+ flag_args = _build_flag_arguments(name, *list_options, **options)
18
+
19
+ _add_flag(flag_args) { |value| _flag_variables[name] = value }
20
+ nil
21
+ end
22
+
23
+ def _build_flag_arguments(name, *list_options, label: nil)
24
+ bool = list_options.include?(:bool)
25
+ shorten = list_options.include?(:shorten)
26
+ one_char_only = name.length == 1
27
+
28
+ flag_args = []
29
+ flag_args << _long_flag_for(name, bool) unless one_char_only
30
+ flag_args << _short_flag_for(name, bool) if shorten || one_char_only
31
+ flag_args << (label || "Sets #{name}")
32
+ flag_args
33
+ end
34
+
35
+ def _short_flag_for(name, bool)
36
+ "-#{name[0]}#{'ARG' unless bool}"
37
+ end
38
+
39
+ def _long_flag_for(name, bool)
40
+ "--#{name}#{'=ARG' unless bool}"
41
+ end
42
+
43
+ def _add_flag(flag_args, &block)
44
+ _flags[flag_args] = block
45
+ end
46
+
47
+ def _flags
48
+ @_flags ||= {}
49
+ end
50
+
51
+ def _flag_variables
52
+ @_flag_variables ||= {}
53
+ end
54
+ end
55
+ end
56
+ end
data/lib/core/main.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Main
4
+ class << self
5
+ def execute
6
+ MainPipeline::BuilderRunner
7
+ .call(builder, main_options(builder))
8
+ .error { exit(_1[:exit_status]) }
9
+ end
10
+
11
+ private
12
+
13
+ def path
14
+ @path ||=
15
+ MainPipeline::PathFetcher
16
+ .call(user_builders_dir)
17
+ .error { exit_without_builder(1) }
18
+ .ok
19
+ end
20
+
21
+ def user_builders_dir
22
+ @user_builders_dir ||= Pathname.new(Dir.pwd)
23
+ end
24
+
25
+ def builder
26
+ @builder ||=
27
+ MainPipeline::BuilderFetcher
28
+ .call(path, user_builders_dir)
29
+ .ok
30
+ end
31
+
32
+ def main_options(target_builder = nil)
33
+ MainPipeline::ParseOptions
34
+ .call(target_builder)
35
+ .error { exit(1) }
36
+ .ok
37
+ end
38
+
39
+ def exit_without_builder(status = 0)
40
+ main_options
41
+ exit(status)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MainPipeline
4
+ class BuilderFetcher < Service::Base
5
+ def call(path, user_builders_dir)
6
+ cls = path
7
+ .relative_path_from(user_builders_dir)
8
+ .each_filename
9
+ .to_a
10
+ .tap { |parts| parts[-1] = parts[-1].delete_suffix('.rb') }
11
+ .map { |p| p.split('_').map!(&:capitalize).join }
12
+ .inject(Object) { |acc, name| acc.const_get(name) }
13
+ ok(cls.new)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ module MainPipeline
5
+ class BuilderRunner < Service::Base
6
+ def call(builder, main_options)
7
+ curl_command = builder.build(**main_options)
8
+ verbose_output(curl_command) if main_options[:verbose]
9
+
10
+ run(curl_command)
11
+ ok
12
+ end
13
+
14
+ private
15
+
16
+ def run(curl_command)
17
+ system(curl_command)
18
+ return error({ exit_status: $CHILD_STATUS.exitstatus }) unless $CHILD_STATUS.success?
19
+
20
+ puts
21
+ end
22
+
23
+ def verbose_output(curl_command)
24
+ puts "\e[32mRunning:\e[0m"
25
+ puts curl_command
26
+ puts "\e[34mOutput:\e[0m"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MainPipeline
4
+ class ParseOptions < Service::Base
5
+ def call(builder)
6
+ # NOTE: the line below is necessary to load flags in builder#execute
7
+ builder&.execute
8
+
9
+ main_options = {}
10
+ option_parser = build_option_parser(builder, main_options)
11
+ return unless option_parser
12
+
13
+ option_parser.parse!
14
+
15
+ ok(main_options)
16
+ rescue OptionParser::InvalidOption => e
17
+ puts e.message
18
+ error
19
+ end
20
+
21
+ private
22
+
23
+ def build_option_parser(builder, main_options)
24
+ OptionParser.new do |opts|
25
+ opts.banner = 'Usage: gurl FILE [options]'
26
+ opts.separator ''
27
+ opts.separator 'Global options:'
28
+ opts.on('-v', '--verbose', 'Enable verbose output') { main_options[:verbose] = true }
29
+ opts.on('--one-line', 'Generate cURL command as a single line') { main_options[:one_line] = true }
30
+ add_help_flag(opts)
31
+ add_builder_flags(opts, builder) if builder
32
+ end
33
+ end
34
+
35
+ def add_builder_flags(opts, builder)
36
+ opts.separator ''
37
+ opts.separator 'Builder-specific flags:'
38
+ builder.process_flags(opts)
39
+ end
40
+
41
+ def add_help_flag(opts)
42
+ opts.on('-h', '--help', 'Show this help message') do
43
+ puts opts
44
+ exit
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MainPipeline
4
+ class PathFetcher < Service::Base
5
+ def call(user_builders_dir)
6
+ return if argv_empty?
7
+
8
+ arg_path = ARGV[0]
9
+ arg_path += '.rb' unless arg_path.end_with?('.rb')
10
+
11
+ path = user_builders_dir + arg_path
12
+ return unless path_exists?(path)
13
+
14
+ ok(path.realpath)
15
+ end
16
+
17
+ def path_exists?(path)
18
+ return true if path.exist?
19
+
20
+ puts "File does not exist: #{path}"
21
+ error
22
+ false
23
+ end
24
+
25
+ def argv_empty?
26
+ return false if ARGV.any?
27
+
28
+ puts 'Path not specified'
29
+ error
30
+ true
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Service
4
+ class Base
5
+ def self.call(*args)
6
+ instance = new
7
+ instance.call(*args)
8
+ instance.response
9
+ end
10
+
11
+ attr_reader :response
12
+
13
+ private
14
+
15
+ def error(error_data = nil)
16
+ @response = Response.new(:error, error_data)
17
+ end
18
+
19
+ def ok(success_data = nil)
20
+ @response = Response.new(:ok, success_data)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Service
4
+ class Response
5
+ attr_reader :data
6
+
7
+ def initialize(status, data)
8
+ @status = status
9
+ @data = data
10
+ end
11
+
12
+ def ok
13
+ yield(data) if ok? && block_given?
14
+ data
15
+ end
16
+
17
+ def error
18
+ yield(data) if error? && block_given?
19
+ self
20
+ end
21
+
22
+ def ok?
23
+ @status == :ok
24
+ end
25
+
26
+ def error?
27
+ @status == :error
28
+ end
29
+ end
30
+ end
data/lib/gurl.rb ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'pathname'
5
+ require 'open3'
6
+ require 'optparse'
7
+ require 'bundler/setup'
8
+ require 'zeitwerk'
9
+ Bundler.require(:default)
10
+
11
+ loader = Zeitwerk::Loader.new
12
+ loader.push_dir(Dir.pwd)
13
+ loader.push_dir(File.join(__dir__, 'core'))
14
+ loader.setup
15
+
16
+ Main.execute
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gurl
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre.alpha
5
+ platform: ruby
6
+ authors:
7
+ - Hikari Luz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: zeitwerk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.6'
27
+ description: Utility tool for building and executing cURL commands using Ruby.
28
+ email: hikaridesuyoo@gmail.com
29
+ executables:
30
+ - gurl
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/gurl
35
+ - lib/core/builder/base.rb
36
+ - lib/core/builder/base_support/flag_concern.rb
37
+ - lib/core/main.rb
38
+ - lib/core/main_pipeline/builder_fetcher.rb
39
+ - lib/core/main_pipeline/builder_runner.rb
40
+ - lib/core/main_pipeline/parse_options.rb
41
+ - lib/core/main_pipeline/path_fetcher.rb
42
+ - lib/core/service/base.rb
43
+ - lib/core/service/response.rb
44
+ - lib/gurl.rb
45
+ homepage:
46
+ licenses:
47
+ - GPL-3.0
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 3.0.0
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">"
61
+ - !ruby/object:Gem::Version
62
+ version: 1.3.1
63
+ requirements: []
64
+ rubygems_version: 3.4.10
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Build cURL commands with Ruby
68
+ test_files: []