poke-your-api 0.2.7 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c406973279d9a2c5d5045a0897a2f9fa944d6954cb036586189dfcd85d13ac8
4
- data.tar.gz: 278140c048ff323f8bbab40d20a04a0fafc68a31b3441187158a733ec6b62f20
3
+ metadata.gz: 54d9c5d820d46a25b19322bf0b8208a79bff75aff71854354a1f755e3ed998f5
4
+ data.tar.gz: f343b6aa3e2d9e4c44f5e4a1273d7d3632fbb5799f67c0d56091254c524174a7
5
5
  SHA512:
6
- metadata.gz: f7e9e8c28fcf44d52bac31d27f3a9d243771d221bb801790b5323417856059af953e1039bc90fa73d651f295a47e1ca852069683cbd34ece5fa3e1137ea7cd1c
7
- data.tar.gz: 6ea22db23dc5754718b53dc01c45f77800f1da7615e591ed6f0d92dbe76d8feb814c5106acb803e695d83627832681c7444b74081a52c2d0380eb539c93cea62
6
+ metadata.gz: 5f1809cfd27e99c1a1f1742b79f3b405b548ea0d87fe1e1b403b557677c4f60b41beab68d5613271b5891b8b4532e8f961421d6bdb8f958ee2529e8b25bebed8
7
+ data.tar.gz: d89ddcce79212dfafe919eceb3106aef349ce15209ce39c10d4424d2c80bf690dede87b5e485b5b96397a07ab81a254515ee72ff39d618a505b644a02cc3fd4f
data/lib/poke/cli.rb CHANGED
@@ -24,6 +24,8 @@ module Poke
24
24
  desc 'env', 'Display and edit environments'
25
25
  method_option :help, aliases: '-h', type: :boolean,
26
26
  desc: 'Display usage information'
27
+ method_option :open, aliases: '-o', type: :boolean,
28
+ desc: 'Open config in the editor'
27
29
  def env(*)
28
30
  if options[:help]
29
31
  invoke :help, ['env']
@@ -33,6 +35,17 @@ module Poke
33
35
  end
34
36
  end
35
37
 
38
+ desc 'new', 'Create a new request'
39
+ method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
40
+ def new(*)
41
+ if options[:help]
42
+ invoke :help, ['new']
43
+ else
44
+ require_relative 'commands/new'
45
+ Poke::Commands::New.new(options).execute
46
+ end
47
+ end
48
+
36
49
  desc 'curl', 'Find and execute a request'
37
50
  method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
38
51
  method_option :env, aliases: '-e', type: :string, desc: 'Set target environment'
@@ -5,6 +5,7 @@ require_relative '../group'
5
5
  require_relative '../config'
6
6
  require_relative '../paint'
7
7
  require_relative '../last_recently_used'
8
+ require_relative '../curl_parser'
8
9
 
9
10
  require 'pathname'
10
11
  require 'pastel'
@@ -96,16 +97,14 @@ module Poke
96
97
  end
97
98
 
98
99
  def build_command(path)
99
- out = TTY::Command.new(printer: :null).run("cat #{path}").out
100
- out << "\n" unless out[-1] == "\n"
101
-
102
- command_lines = out.split(/ \\\n|\n/).map(&:strip)
103
-
104
- comments = command_lines.filter { |line| line.start_with?('#') }.join(" \\\n")
105
- command_lines = command_lines.reject { |line| line.start_with?('#') }
106
- command_lines += additional_curl_params
107
-
108
- [command_lines.join(" \\\n "), comments]
100
+ content = File.read(path)
101
+ parser = CurlParser.new(content)
102
+
103
+ # Add our additional curl parameters
104
+ parser.add_argument("-o #{Poke::Config.response_path}")
105
+ parser.add_argument('-w "%{json}"')
106
+
107
+ [parser.to_command_with_line_continuation, parser.comments.join("\n")]
109
108
  end
110
109
 
111
110
  # rubocop:disable Style/FormatStringToken
@@ -2,16 +2,10 @@
2
2
 
3
3
  require_relative '../command'
4
4
  require_relative '../group'
5
- require_relative '../paint'
6
5
 
7
- require 'pastel'
8
-
9
- require 'tty-box'
10
- require 'tty-command'
11
- require 'tty-cursor'
6
+ require 'json'
12
7
  require 'tty-editor'
13
8
  require 'tty-prompt'
14
- require 'tty-table'
15
9
 
16
10
  module Poke
17
11
  module Commands
@@ -23,60 +17,10 @@ module Poke
23
17
  group_name = TTY::Prompt.new.select('Select the group', groups.map(&:name), filter: true, quiet: true)
24
18
  group = groups.find { |g| g.name == group_name }
25
19
 
26
- manage_group_config(output:, group:)
27
- end
28
-
29
- def cursor
30
- @cursor ||= TTY::Cursor
31
- end
32
-
33
- def pastel
34
- @pastel ||= Pastel.new
35
- end
36
-
37
- def manage_group_config(output:, group:)
38
- output << cursor.save
39
-
40
- render(output, group)
41
-
42
- loop do
43
- case TTY::Prompt.new.keypress('', quiet: true)
44
- when 'c'
45
- group.config.default_env = TTY::Prompt.new.select(
46
- 'Select new default environment', group.config.envs,
47
- filter: true, quiet: true
48
- )
49
- group.save_config!
50
-
51
- render(output, group)
52
- when 'e'
53
- TTY::Editor.open(group.config_path)
54
- break
55
- when 'q'
56
- break
57
- end
58
- end
59
- end
60
-
61
- def render(output, group)
62
- output << cursor.restore
63
- output << cursor.clear_screen_down
64
-
65
- title = ['| ', pastel.decorate(group.name.upcase, :yellow, :underline, :bold), ' |'].join
66
- footer = '| q → quit | c → change default env | e → edit config file |'
67
- table = TTY::Table.new(
68
- header: ['env \\ var', *group.config.variables(group.config.default_env).keys],
69
- rows: group.config.envs.map do |name|
70
- [name, *group.config.variables(name).values]
71
- end
72
- )
73
-
74
- # output << "\n"
75
- output << TTY::Box.frame(align: :center, title: { top_center: title, bottom_center: footer }) do
76
- [
77
- "(default env: #{group.config.default_env})\n\n",
78
- table.render(:unicode, multiline: true, padding: [0, 1, 0, 1])
79
- ].join('')
20
+ if @options[:open]
21
+ TTY::Editor.open(group.config_path)
22
+ else
23
+ output.puts JSON.pretty_generate(group.config.to_h)
80
24
  end
81
25
  end
82
26
  end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../config'
5
+
6
+ require 'fileutils'
7
+ require 'pathname'
8
+ require 'tty-editor'
9
+ require 'tty-prompt'
10
+
11
+ module Poke
12
+ module Commands
13
+ class New < Poke::Command
14
+ CREATE_NEW_DIR = :create_new_dir
15
+ TEMPLATE = "curl $BASE_URL/get -G -d foo=bar\n".freeze
16
+
17
+ private
18
+
19
+ def run(output: $stdout, errors: $stderr)
20
+ unless Config.valid?
21
+ errors << "Config not initialized. Run `poke init` first.\n"
22
+ return
23
+ end
24
+
25
+ prompt = TTY::Prompt.new(interrupt: :exit)
26
+ directory = choose_directory(prompt)
27
+ filename = choose_filename(prompt)
28
+ request_path = build_request_path(directory, filename)
29
+
30
+ if File.exist?(request_path) && !prompt.yes?('Request exists. Overwrite?')
31
+ output << "Canceled.\n"
32
+ return
33
+ end
34
+
35
+ File.write(request_path, TEMPLATE)
36
+ TTY::Editor.open(request_path)
37
+ output << "Created request: #{request_path}\n"
38
+ rescue ArgumentError => e
39
+ errors << "#{e.message}\n"
40
+ end
41
+
42
+ def choose_directory(prompt)
43
+ choices = discover_directories
44
+ labeled_choices = choices.map { |path| { name: relative_path(path), value: path } }
45
+ labeled_choices << { name: '[Create new directory]', value: CREATE_NEW_DIR }
46
+
47
+ selection = prompt.select(
48
+ 'Select directory under ~/.poke',
49
+ labeled_choices,
50
+ filter: true,
51
+ quiet: true
52
+ )
53
+
54
+ return selection unless selection == CREATE_NEW_DIR
55
+
56
+ input = prompt.ask('New directory path under ~/.poke', required: true)
57
+ target = build_target_path(input)
58
+ FileUtils.mkdir_p(target)
59
+ target
60
+ end
61
+
62
+ def choose_filename(prompt)
63
+ input = prompt.ask('Request name', required: true)&.strip
64
+ raise ArgumentError, 'Request name cannot be empty' if input.nil? || input.empty?
65
+ raise ArgumentError, 'Request name must not include path separators' if input.include?('/')
66
+
67
+ name = input.gsub(/\.curl\z/, '')
68
+ "#{name}.curl"
69
+ end
70
+
71
+ def discover_directories
72
+ root = File.expand_path(Config.root_path)
73
+
74
+ Dir.glob("#{root}/**/")
75
+ .map { |path| File.expand_path(path).sub(%r{/\z}, '') }
76
+ .uniq
77
+ .reject { |path| path == root }
78
+ .sort
79
+ end
80
+
81
+ def build_request_path(directory, filename)
82
+ target = Pathname.new(File.join(directory, filename)).cleanpath.to_s
83
+ ensure_inside_root!(target)
84
+ target
85
+ end
86
+
87
+ def build_target_path(relative_input)
88
+ target = Pathname.new(File.expand_path(File.join(Config.root_path, relative_input.to_s))).cleanpath.to_s
89
+
90
+ raise ArgumentError, 'Directory must be a subdirectory of ~/.poke' if target == File.expand_path(Config.root_path)
91
+
92
+ ensure_inside_root!(target)
93
+ target
94
+ end
95
+
96
+ def ensure_inside_root!(path)
97
+ root = File.expand_path(Config.root_path)
98
+ root_prefix = "#{root}/"
99
+ return if path.start_with?(root_prefix)
100
+
101
+ raise ArgumentError, 'Path must stay inside ~/.poke'
102
+ end
103
+
104
+ def relative_path(path)
105
+ Pathname.new(path).relative_path_from(Pathname.new(File.expand_path(Config.root_path))).to_s
106
+ end
107
+ end
108
+ end
109
+ end
@@ -5,6 +5,7 @@ require_relative '../group'
5
5
  require_relative '../config'
6
6
  require_relative '../paint'
7
7
  require_relative '../last_recently_used'
8
+ require_relative '../curl_parser'
8
9
 
9
10
  require 'pathname'
10
11
  require 'pastel'
@@ -76,18 +77,15 @@ module Poke
76
77
  errors << "Run count: #{count}\nAverage time: #{format('%.2fms', (times.sum / times.size * 1000))}\n\n"
77
78
  end
78
79
 
79
- # extract
80
80
  def build_command(path)
81
- out = TTY::Command.new(printer: :null).run("cat #{path}").out
82
- out << "\n" unless out[-1] == "\n"
83
-
84
- command_lines = out.split(/ \\\n|\n/).map(&:strip)
85
-
86
- comments = command_lines.filter { |line| line.start_with?('#') }.join(" \\\n")
87
- command_lines = command_lines.reject { |line| line.start_with?('#') }
88
- command_lines += additional_curl_params
89
-
90
- [command_lines.join(" \\\n "), comments]
81
+ content = File.read(path)
82
+ parser = CurlParser.new(content)
83
+
84
+ # Add our additional curl parameters
85
+ parser.add_argument("-o #{Poke::Config.response_path}")
86
+ parser.add_argument('-w "%{json}"')
87
+
88
+ [parser.to_command_with_line_continuation, parser.comments.join("\n")]
91
89
  end
92
90
 
93
91
  # rubocop:disable Style/FormatStringToken
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Poke
6
+ class CurlParser
7
+ attr_reader :comments, :arguments, :url
8
+
9
+ def initialize(content)
10
+ @content = content
11
+ @comments = []
12
+ @arguments = []
13
+ @url = nil
14
+ parse
15
+ end
16
+
17
+ def to_command
18
+ parts = ['curl']
19
+ parts.concat(@arguments)
20
+ parts << @url if @url
21
+ parts.join(' ')
22
+ end
23
+
24
+ def to_command_with_line_continuation
25
+ parts = ['curl']
26
+ parts.concat(@arguments)
27
+ parts << @url if @url
28
+
29
+ # Join with line continuation for better readability
30
+ parts.join(" \\\n ")
31
+ end
32
+
33
+ def add_argument(arg)
34
+ @arguments << arg
35
+ end
36
+
37
+ def remove_argument(pattern)
38
+ @arguments.reject! { |arg| arg.match?(pattern) }
39
+ end
40
+
41
+ def replace_argument(pattern, replacement)
42
+ @arguments.map! do |arg|
43
+ arg.match?(pattern) ? replacement : arg
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def parse
50
+ # Normalize line endings and ensure content ends with newline
51
+ content = @content.gsub(/\r\n/, "\n").gsub(/\r/, "\n")
52
+ content += "\n" unless content.end_with?("\n")
53
+
54
+ # First, extract comments
55
+ extract_comments(content)
56
+
57
+ # Then parse the curl command
58
+ parse_curl_content(content)
59
+ end
60
+
61
+ def extract_comments(content)
62
+ content.each_line do |line|
63
+ stripped = line.strip
64
+ @comments << stripped if stripped.start_with?('#')
65
+ end
66
+ end
67
+
68
+ def parse_curl_content(content)
69
+ # Remove comments and empty lines
70
+ curl_lines = content.lines.reject do |line|
71
+ line.strip.empty? || line.strip.start_with?('#')
72
+ end
73
+
74
+ return if curl_lines.empty?
75
+
76
+ # Join lines with continuations
77
+ curl_content = join_continuations(curl_lines)
78
+
79
+ # Parse the curl command
80
+ parse_curl_command(curl_content)
81
+ end
82
+
83
+ def join_continuations(lines)
84
+ result = ""
85
+ i = 0
86
+
87
+ while i < lines.length
88
+ line = lines[i].chomp
89
+
90
+ # Check if this line ends with continuation
91
+ if line.end_with?('\\')
92
+ result += line[0...-1] + " "
93
+ i += 1
94
+ else
95
+ result += line + " "
96
+ i += 1
97
+ end
98
+ end
99
+
100
+ result.strip
101
+ end
102
+
103
+ def parse_curl_command(content)
104
+ # Remove 'curl' command if present
105
+ content = content.sub(/^\s*curl\s+/, '')
106
+
107
+ # Split arguments while preserving quoted strings
108
+ args = split_arguments(content)
109
+
110
+ args.each do |arg|
111
+ if arg.start_with?('-')
112
+ @arguments << arg
113
+ elsif @url.nil?
114
+ @url = arg
115
+ else
116
+ # Additional arguments after URL
117
+ @arguments << arg
118
+ end
119
+ end
120
+ end
121
+
122
+ def split_arguments(line)
123
+ args = []
124
+ current_arg = ""
125
+ in_quotes = false
126
+ quote_char = nil
127
+ i = 0
128
+
129
+ while i < line.length
130
+ char = line[i]
131
+
132
+ if !in_quotes && char.match?(/\s/)
133
+ if !current_arg.empty?
134
+ args << current_arg
135
+ current_arg = ""
136
+ end
137
+ elsif !in_quotes && (char == '"' || char == "'")
138
+ in_quotes = true
139
+ quote_char = char
140
+ current_arg += char
141
+ elsif in_quotes && char == quote_char
142
+ in_quotes = false
143
+ quote_char = nil
144
+ current_arg += char
145
+ else
146
+ current_arg += char
147
+ end
148
+
149
+ i += 1
150
+ end
151
+
152
+ args << current_arg unless current_arg.empty?
153
+ args
154
+ end
155
+ end
156
+ end
@@ -39,5 +39,9 @@ module Poke
39
39
  def variables(env)
40
40
  @values['envs'][env]
41
41
  end
42
+
43
+ def to_h
44
+ @values
45
+ end
42
46
  end
43
47
  end
data/lib/poke/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Poke
4
- VERSION = '0.2.7'
4
+ VERSION = '0.4.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poke-your-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Bator
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-06 00:00:00.000000000 Z
11
+ date: 2026-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pastel
@@ -129,9 +129,11 @@ files:
129
129
  - lib/poke/commands/lru.rb
130
130
  - lib/poke/commands/lru/cat.rb
131
131
  - lib/poke/commands/lru/reset.rb
132
+ - lib/poke/commands/new.rb
132
133
  - lib/poke/commands/response.rb
133
134
  - lib/poke/commands/speed.rb
134
135
  - lib/poke/config.rb
136
+ - lib/poke/curl_parser.rb
135
137
  - lib/poke/group.rb
136
138
  - lib/poke/group_config.rb
137
139
  - lib/poke/last_recently_used.rb