sqlui 0.1.70 → 0.1.72

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: 476cdcf4c9f35aa0b46a5d8dd3f41aa409a4ef5cf72bb0d02099ea44d4eb92e3
4
- data.tar.gz: f52996d5793db9b6529cf7f1c8f5dd1ee171bd010f00419604e2399d430eb811
3
+ metadata.gz: d93fb898461c1843f631c16ba879898e54e12374c89bdc49f702420e06d7789e
4
+ data.tar.gz: 875364afb309801a644900301051cdbe96939d12aa3980eb2e1460668e032b24
5
5
  SHA512:
6
- metadata.gz: f37ae3ed5654474330b6ce34a432cbe55c87911494819aeb46e279cf22246b48d3afeddeeb7a4f2f69bee8e39e4186d2feab86c1dc8405e7b3f149a9e09a8d87
7
- data.tar.gz: a8eaad53be8c5740cc63fc0a5d2017a4ff3070fc90521381e3f8d2dce99d5ea399dce493544e4112579a89b0da925c606328739b1b89c137bbeeb1e865bb1af0
6
+ metadata.gz: 3868ba4eac83d9584e6da037ee34e475010b47e0ff4dcdb791837f81125a77f4090c38fd60360f1a12f4fd2f3c5402092b4a936c5eb670c15ab1518583c04b3e
7
+ data.tar.gz: f9c9c07fdb3016aec05fb2f99c5d307eafc95b2fa9d4d693afb83af71a521d5fa32c9b03137c1f30293218186f432a2eff1a28490290f974b25163126c399fea
data/.release-version CHANGED
@@ -1 +1 @@
1
- 0.1.70
1
+ 0.1.72
data/app/github/cache.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
4
+
3
5
  require_relative '../checks'
4
6
 
5
7
  module Github
@@ -26,8 +28,6 @@ module Github
26
28
  end
27
29
 
28
30
  def [](key)
29
- check_non_empty_string(key: key)
30
-
31
31
  @mutex.synchronize do
32
32
  evict
33
33
  if (value = @hash[key])
@@ -41,7 +41,6 @@ module Github
41
41
  end
42
42
 
43
43
  def []=(key, value)
44
- check_non_empty_string(key: key)
45
44
  check_is_a(value: [Entry, value])
46
45
 
47
46
  @mutex.synchronize do
@@ -20,7 +20,7 @@ module Github
20
20
  check_positive_integer(cache_for: cache_for)
21
21
 
22
22
  if (cache_entry = @cache[url])
23
- return cache_entry.value
23
+ return cache_entry.value.deep_dup
24
24
  end
25
25
 
26
26
  response = @client.get(url)
@@ -32,5 +32,9 @@ module Github
32
32
  check_non_empty_string(url: url)
33
33
  @client.get(url)
34
34
  end
35
+
36
+ def post(url, request)
37
+ @client.post(url, request)
38
+ end
35
39
  end
36
40
  end
data/app/github/client.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
3
4
  require 'json'
4
5
  require 'logger'
5
6
  require 'rest-client'
@@ -28,9 +29,35 @@ module Github
28
29
  'X-GitHub-Api-Version' => '2022-11-28'
29
30
  }
30
31
  )
31
- raise "get #{url} failed: #{response}" unless response.code == 200
32
+
33
+ raise "GET #{url} returned #{response.code}, expected 200: #{response}" unless response.code == 200
34
+
35
+ JSON.parse(response)
36
+ rescue RestClient::RequestFailed => e
37
+ @logger.error("#{self.class} GET #{url} failed: #{e.response.code} #{e.response}")
38
+ raise e
39
+ end
40
+
41
+ def post(url, request)
42
+ check_non_empty_string(url: url)
43
+ check_is_a(request: [Hash, request])
44
+
45
+ @logger.info "#{self.class} POST #{url}"
46
+ response = RestClient.post(
47
+ url,
48
+ request.to_json,
49
+ {
50
+ 'Accept' => 'application/vnd.github+json',
51
+ 'Authorization' => "Bearer #{@access_token}",
52
+ 'X-GitHub-Api-Version' => '2022-11-28'
53
+ }
54
+ )
55
+ raise "POST #{url} failed: #{response.code} #{response}" unless response.code == 201
32
56
 
33
57
  JSON.parse(response)
58
+ rescue RestClient::RequestFailed => e
59
+ @logger.error("#{self.class} POST #{url} failed: #{e.response.code} #{e.response}")
60
+ raise e
34
61
  end
35
62
  end
36
63
  end
data/app/github/file.rb CHANGED
@@ -7,13 +7,24 @@ module Github
7
7
  class File
8
8
  include Checks
9
9
 
10
- attr_reader :display_path, :content, :github_url
10
+ attr_reader :full_path, :path, :tree_sha, :content, :github_url
11
11
 
12
- def initialize(owner:, repo:, branch:, path:, content:)
13
- check_non_empty_string(owner: owner, repo: repo, branch: branch, path: path)
12
+ def initialize(owner:, repo:, ref:, tree_sha:, path:, content:)
13
+ check_non_empty_string(owner: owner, repo: repo, ref: ref, path: path, tree_sha: tree_sha)
14
+ @full_path = "#{owner}/#{repo}/#{ref}/#{path}"
15
+ @path = path
16
+ @tree_sha = tree_sha
14
17
  @content = check_is_a(content: [String, content])
15
- @display_path = "#{owner}/#{repo}/#{branch}/#{path}"
16
- @github_url = "https://github.com/#{owner}/#{repo}/blob/#{branch}/#{path}"
18
+ @github_url = "https://github.com/#{owner}/#{repo}/blob/#{ref}/#{path}"
19
+ end
20
+
21
+ def ==(other)
22
+ self.class == other.class &&
23
+ @full_path == other.full_path &&
24
+ @path == other.path &&
25
+ @tree_sha == other.tree_sha &&
26
+ @content == other.content &&
27
+ @github_url == other.github_url
17
28
  end
18
29
  end
19
30
  end
data/app/github/paths.rb CHANGED
@@ -1,21 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../checks'
4
+
3
5
  module Github
4
6
  # Methods for dealing with GitHub paths.
5
7
  class Paths
6
- # "<owner>/<repo>/<ref>/<some_path>"
7
- PATH_PATTERN = %r{^(?:[^/]+/){3}.*[^/]$}
8
- private_constant :PATH_PATTERN
8
+ class << self
9
+ include Checks
10
+
11
+ # "<owner>/<repo>/<ref>/<some_path>"
12
+ PATH_PATTERN = %r{^(?:[^/]+/){3}.*[^/]$}
13
+ private_constant :PATH_PATTERN
9
14
 
10
- # Parses a path like "<owner>/<repo>/<ref>/<some_path>" into owner, repo, ref, path.
11
- def self.parse_file_path(path)
12
- check_non_empty_string(path: path)
13
- raise "invalid path: #{path}" unless PATH_PATTERN.match?(path)
15
+ # Parses a path like "<owner>/<repo>/<ref>/<some_path>" into owner, repo, ref, path.
16
+ def parse_file_path(path)
17
+ check_non_empty_string(path: path)
18
+ raise "invalid path: #{path}" unless PATH_PATTERN.match?(path)
14
19
 
15
- owner, repo, ref, *path = path.split('/')
16
- path = path.join('/')
20
+ owner, repo, ref, *path = path.split('/')
21
+ path = path.join('/')
17
22
 
18
- [owner, repo, ref, path]
23
+ [owner, repo, ref, path]
24
+ end
19
25
  end
20
26
  end
21
27
  end
data/app/github/tree.rb CHANGED
@@ -9,9 +9,12 @@ module Github
9
9
  include Checks
10
10
  include Enumerable
11
11
 
12
- attr_reader :truncated
12
+ attr_reader :owner, :repo, :ref, :truncated, :files
13
13
 
14
- def initialize(files:, truncated: false)
14
+ def initialize(owner:, repo:, ref:, files:, truncated: false)
15
+ @owner = check_non_empty_string(owner: owner)
16
+ @repo = check_non_empty_string(repo: repo)
17
+ @ref = check_non_empty_string(ref: ref)
15
18
  @files = check_enumerable_of(files, File)
16
19
  @truncated = check_boolean(truncated: truncated)
17
20
  end
@@ -19,22 +22,45 @@ module Github
19
22
  class << self
20
23
  include Checks
21
24
 
22
- def for(owner:, repo:, branch:, tree_response:)
23
- check_non_empty_string(owner: owner, repo: repo, branch: branch)
25
+ def for(owner:, repo:, ref:, tree_response:)
26
+ check_non_empty_string(owner: owner, repo: repo, ref: ref)
24
27
  check_is_a(tree_response: [Hash, tree_response])
25
28
 
26
29
  truncated = check_boolean(truncated: tree_response['truncated'])
27
30
  tree = check_is_a(tree: [Array, tree_response['tree']])
28
31
  files = tree.map do |blob|
29
- File.new(owner: owner, repo: repo, branch: branch, path: blob['path'], content: blob['content'])
32
+ File.new(
33
+ owner: owner,
34
+ repo: repo,
35
+ ref: ref,
36
+ tree_sha: tree_response['sha'],
37
+ path: blob['path'],
38
+ content: blob['content']
39
+ )
30
40
  end
31
41
 
32
- Tree.new(files: files, truncated: truncated)
42
+ Tree.new(owner: owner, repo: repo, ref: ref, files: files, truncated: truncated)
33
43
  end
34
44
  end
35
45
 
46
+ def [](path)
47
+ @files.find { |f| f.path == path }
48
+ end
49
+
36
50
  def each(&block)
37
51
  @files.each(&block)
38
52
  end
53
+
54
+ def ==(other)
55
+ self.class == other.class &&
56
+ @files == other.files &&
57
+ @truncated == other.truncated
58
+ end
59
+
60
+ def <<(file)
61
+ check_is_a(file: [File, file])
62
+
63
+ @files << file unless @files.include?(file)
64
+ end
39
65
  end
40
66
  end
@@ -4,8 +4,11 @@ require 'base64'
4
4
  require 'concurrent/executor/fixed_thread_pool'
5
5
  require 'json'
6
6
  require 'logger'
7
+ require 'rest-client'
7
8
 
8
9
  require_relative 'caching_client'
10
+ require_relative 'client'
11
+ require_relative 'paths'
9
12
  require_relative 'tree'
10
13
  require_relative '../checks'
11
14
  require_relative '../count_down_latch'
@@ -22,10 +25,7 @@ module Github
22
25
  end
23
26
 
24
27
  DEFAULT_MAX_TREE_CACHE_AGE_SECONDS = 60 * 5 # 5 minutes
25
- private_constant :DEFAULT_MAX_TREE_CACHE_AGE_SECONDS
26
-
27
28
  DEFAULT_MAX_FILE_CACHE_AGE_SECONDS = 60 * 60 * 24 * 7 # 1 week
28
- private_constant :DEFAULT_MAX_FILE_CACHE_AGE_SECONDS
29
29
 
30
30
  MAX_TREE_SIZE = 50
31
31
  private_constant :MAX_TREE_SIZE
@@ -38,14 +38,18 @@ module Github
38
38
  @logger = check_non_nil(logger: logger)
39
39
  end
40
40
 
41
- def get_tree(owner:, repo:, branch:, regex:)
42
- check_non_empty_string(owner: owner, repo: repo, branch: branch)
41
+ def get_tree(owner:, repo:, ref:, regex:, cache: true)
42
+ check_non_empty_string(owner: owner, repo: repo, ref: ref)
43
43
  check_is_a(regex: [Regexp, regex])
44
44
 
45
- response = @client.get_with_caching(
46
- "https://api.github.com/repos/#{owner}/#{repo}/git/trees/#{branch}?recursive=true",
47
- cache_for: DEFAULT_MAX_TREE_CACHE_AGE_SECONDS
48
- )
45
+ response = if cache
46
+ @client.get_with_caching(
47
+ "https://api.github.com/repos/#{owner}/#{repo}/git/trees/#{ref}?recursive=true",
48
+ cache_for: DEFAULT_MAX_TREE_CACHE_AGE_SECONDS
49
+ )
50
+ else
51
+ @client.get_without_caching("https://api.github.com/repos/#{owner}/#{repo}/git/trees/#{ref}?recursive=true")
52
+ end
49
53
 
50
54
  response['tree'] = response['tree'].select { |blob| regex.match?(blob['path']) }
51
55
  raise "trees with more than #{MAX_TREE_SIZE} blobs not supported" if response['tree'].size > MAX_TREE_SIZE
@@ -63,7 +67,72 @@ module Github
63
67
  latch.await(timeout: 10)
64
68
  raise 'failed to load saved files' unless response['tree'].all? { |blob| blob['content'] }
65
69
 
66
- Tree.for(owner: owner, repo: repo, branch: branch, tree_response: response)
70
+ Tree.for(owner: owner, repo: repo, ref: ref, tree_response: response)
71
+ end
72
+
73
+ def create_commit_with_file(owner:, repo:, base_sha:, branch:, path:, content:, author_name:, author_email:)
74
+ check_non_empty_string(
75
+ owner: owner,
76
+ repo: repo,
77
+ base_sha: base_sha,
78
+ branch: branch,
79
+ path: path,
80
+ content: content,
81
+ author_name: author_name,
82
+ author_email: author_email
83
+ )
84
+
85
+ begin
86
+ @client.get_without_caching("https://api.github.com/repos/#{owner}/#{repo}/git/ref/#{branch}")
87
+ raise "branch already exists: #{owner}/#{repo}/#{branch}"
88
+ rescue RestClient::NotFound
89
+ # ignored
90
+ end
91
+
92
+ blob_response = @client.post(
93
+ "https://api.github.com/repos/#{owner}/#{repo}/git/blobs",
94
+ {
95
+ content: Base64.encode64(content),
96
+ encoding: 'base64'
97
+ }
98
+ )
99
+ tree_response = @client.post(
100
+ "https://api.github.com/repos/#{owner}/#{repo}/git/trees",
101
+ {
102
+ base_tree: base_sha,
103
+ tree: [
104
+ {
105
+ path: path,
106
+ mode: '100644',
107
+ type: 'blob',
108
+ sha: check_non_empty_string(sha: blob_response['sha'])
109
+ }
110
+ ]
111
+ }
112
+ )
113
+ message = path.size > 43 ? "Update [...]#{path[-38..]}" : "Update #{path}"
114
+ commit_response = @client.post(
115
+ "https://api.github.com/repos/#{owner}/#{repo}/git/commits",
116
+ {
117
+ message: message,
118
+ author: {
119
+ name: 'SQLUI',
120
+ email: 'nicholasdower+sqlui@gmail.com',
121
+ date: Time.now.iso8601
122
+ },
123
+ parents: [
124
+ base_sha
125
+ ],
126
+ tree: check_non_empty_string(sha: tree_response['sha'])
127
+ }
128
+ )
129
+ @client.post(
130
+ "https://api.github.com/repos/#{owner}/#{repo}/git/refs",
131
+ {
132
+ ref: "refs/heads/#{branch}",
133
+ sha: check_non_empty_string(sha: commit_response['sha'])
134
+ }
135
+ )
67
136
  end
68
137
 
69
138
  private
@@ -78,16 +147,5 @@ module Github
78
147
  )
79
148
  Base64.decode64(response['content'])
80
149
  end
81
-
82
- # Returns the contents of the file at the specified path. Uses the cache.
83
- def get_file_content_without_caching(owner:, repo:, path:, ref:)
84
- check_non_empty_string(owner: owner, repo: repo, sha: ref, path: path)
85
-
86
- response = @client.get_without_caching(
87
- "https://api.github.com/repos/#{owner}/#{repo}/contents/#{path}?ref=#{ref}"
88
- )
89
-
90
- Base64.decode64(response['content'])
91
- end
92
150
  end
93
151
  end
data/app/pigs.rb ADDED
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ # You know, pig stuff.
4
+ class Pigs
5
+ NAMES = %w[
6
+ babe misery piglet snowball squealer wilbur napoleon porky miss-piggy petunia betina
7
+ belinda pumbaa hamm peppa-pig fifer fiddler practical-pig hampton-j piggy orson pua
8
+ huxley gub-gub olivia piggy-bank arnold boss-hog
9
+ ].freeze
10
+ private_constant :NAMES
11
+
12
+ POSSESSIVE_NAMES = NAMES.map { |name| "#{name}s" }
13
+ private_constant :POSSESSIVE_NAMES
14
+
15
+ NOUNS = %w[snout ear ears tail hoof hooves foot feet].freeze
16
+ private_constant :NOUNS
17
+
18
+ ADJECTIVES = %w[pink hairy plump round cute dirty smelly tiny adorable slimy dainty big cunning sneaky].freeze
19
+ private_constant :ADJECTIVES
20
+
21
+ GERUND_VERBS = %w[oinking squealing grunting rooting sniffing snorting wallowing trotting rolling].freeze
22
+ private_constant :GERUND_VERBS
23
+
24
+ VERBS = %w[oinks squeals grunts roots sniffs snorts wallows trots rolls swallows].freeze
25
+ private_constant :VERBS
26
+
27
+ ADVERBS = %w[hungrily loudly greedily angrily coyly playfully lazily noisily happily].freeze
28
+ private_constant :ADVERBS
29
+
30
+ FOOD_NOUNS = %w[trotter loin cutlet hock skin jowl cheek rump belly ham bacon back shoulder ear ears tail rib
31
+ tenderloin pork-chop roast sausage fatback fat tongue].freeze
32
+ private_constant :FOOD_NOUNS
33
+
34
+ PLURAL_FOOD_NOUNS = %w[ears trotters cutlets hocks jowls cheeks ribs spareribs pork-chops sausages steaks].freeze
35
+ private_constant :PLURAL_FOOD_NOUNS
36
+
37
+ FOOD_ADJECTIVES = %w[tasty meaty greasy chewy salty smoky juicy delicious crunchy fatty salted spicy savory succulent
38
+ moist flavorful tender sweet].freeze
39
+ private_constant :FOOD_ADJECTIVES
40
+
41
+ PIG_ON_PIG_VERBS = %w[snuggles cuddles chases sniffs watches smells cuddles oinks-at squeals-at grunts-at snorts-at
42
+ wallows-with roots-with trots-with rolls-with grins-at].freeze
43
+ private_constant :PIG_ON_PIG_VERBS
44
+
45
+ COMBOS = [
46
+ [NAMES],
47
+ [NAMES, %w[and], NAMES],
48
+ [NAMES, %w[and], NAMES, %w[are], ADJECTIVES],
49
+ [NAMES, PIG_ON_PIG_VERBS, NAMES],
50
+ [GERUND_VERBS, NAMES],
51
+ [NAMES, GERUND_VERBS],
52
+ [NAMES, GERUND_VERBS, ADVERBS],
53
+ [GERUND_VERBS, NAMES, %w[and], NAMES],
54
+ [NAMES, %w[and], NAMES, GERUND_VERBS],
55
+ [NAMES, %w[and], NAMES, GERUND_VERBS, ADVERBS],
56
+ [NAMES, VERBS],
57
+ [ADJECTIVES, NAMES],
58
+ [NAMES, %w[is], ADJECTIVES],
59
+ [ADJECTIVES, %w[and], ADJECTIVES, NAMES],
60
+ [NAMES, VERBS, ADVERBS],
61
+ [POSSESSIVE_NAMES, NOUNS],
62
+ [POSSESSIVE_NAMES, ADJECTIVES, NOUNS],
63
+ [POSSESSIVE_NAMES, ADJECTIVES, %w[and], ADJECTIVES, NOUNS],
64
+ [ADJECTIVES, NOUNS],
65
+ [POSSESSIVE_NAMES, FOOD_ADJECTIVES, FOOD_NOUNS],
66
+ [POSSESSIVE_NAMES, FOOD_ADJECTIVES, PLURAL_FOOD_NOUNS],
67
+ [NAMES, %w[has-a], FOOD_ADJECTIVES, FOOD_NOUNS],
68
+ [NAMES, %w[has], FOOD_ADJECTIVES, PLURAL_FOOD_NOUNS],
69
+ [FOOD_NOUNS],
70
+ [FOOD_ADJECTIVES, FOOD_NOUNS],
71
+ [FOOD_ADJECTIVES, FOOD_ADJECTIVES, FOOD_NOUNS],
72
+ [%w[a one], FOOD_ADJECTIVES, FOOD_NOUNS],
73
+ [%w[a one], FOOD_ADJECTIVES, FOOD_ADJECTIVES, FOOD_NOUNS],
74
+ [PLURAL_FOOD_NOUNS],
75
+ [FOOD_ADJECTIVES, PLURAL_FOOD_NOUNS],
76
+ [FOOD_ADJECTIVES, FOOD_ADJECTIVES, PLURAL_FOOD_NOUNS],
77
+ [%w[some two three four five six seven eight nine ten a-dozen a-plate-of a-bucket-of a-barrel-of],
78
+ PLURAL_FOOD_NOUNS],
79
+ [%w[some two three four five six seven eight nine ten a-dozen a-plate-of a-bucket-of a-barrel-of], FOOD_ADJECTIVES,
80
+ PLURAL_FOOD_NOUNS],
81
+ [%w[some two three four five six seven eight nine ten a-dozen a-plate-of a-bucket-of a-barrel-of], FOOD_ADJECTIVES,
82
+ FOOD_ADJECTIVES, PLURAL_FOOD_NOUNS]
83
+ ].freeze
84
+ private_constant :COMBOS
85
+
86
+ COMBO_COUNTS = COMBOS.map do |combo|
87
+ size = 1
88
+ used = []
89
+ combo.each do |list|
90
+ size *= list.size - used.count(list)
91
+ used << list
92
+ end
93
+ size
94
+ end
95
+ private_constant :COMBO_COUNTS
96
+
97
+ TOTAL_COMBOS = COMBO_COUNTS.sum
98
+ private_constant :TOTAL_COMBOS
99
+
100
+ COMBO_SUM = COMBO_COUNTS.each_with_object([0]).map do |i, j|
101
+ j[0] = j[0] + i
102
+ end
103
+ private_constant :COMBO_SUM
104
+
105
+ def self.total_combos
106
+ TOTAL_COMBOS
107
+ end
108
+
109
+ def self.generate_phrase
110
+ rand = rand(TOTAL_COMBOS)
111
+ COMBOS.each_with_index do |combo, i|
112
+ next unless rand < COMBO_SUM[i]
113
+
114
+ used = []
115
+ parts = []
116
+ combo.each do |list|
117
+ list -= used
118
+ used << list.sample
119
+ parts << used.last
120
+ end
121
+ return parts.join('-')
122
+ end
123
+ end
124
+ end
data/app/saved_config.rb CHANGED
@@ -4,7 +4,7 @@ require_relative 'args'
4
4
 
5
5
  # Config for saved files.
6
6
  class SavedConfig
7
- attr_reader :token, :owner, :repo, :branch, :regex
7
+ attr_reader :token, :owner, :repo, :branch, :regex, :author_name, :author_email
8
8
 
9
9
  def initialize(hash)
10
10
  @token = Args.fetch_non_empty_string(hash, :token).strip
@@ -12,5 +12,8 @@ class SavedConfig
12
12
  @repo = Args.fetch_non_empty_string(hash, :repo).strip
13
13
  @branch = Args.fetch_non_empty_string(hash, :branch).strip
14
14
  @regex = Regexp.new(Args.fetch_non_empty_string(hash, :regex))
15
+ author = Args.fetch_non_empty_hash(hash, :author)
16
+ @author_name = Args.fetch_non_empty_string(author, :name).strip
17
+ @author_email = Args.fetch_non_empty_string(author, :email).strip
15
18
  end
16
19
  end