fs-gitlab 4.18.1

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 (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +270 -0
  3. data/LICENSE.txt +24 -0
  4. data/README.md +260 -0
  5. data/exe/gitlab +11 -0
  6. data/lib/gitlab/api.rb +24 -0
  7. data/lib/gitlab/cli.rb +89 -0
  8. data/lib/gitlab/cli_helpers.rb +243 -0
  9. data/lib/gitlab/client/access_requests.rb +103 -0
  10. data/lib/gitlab/client/application_settings.rb +172 -0
  11. data/lib/gitlab/client/avatar.rb +21 -0
  12. data/lib/gitlab/client/award_emojis.rb +137 -0
  13. data/lib/gitlab/client/boards.rb +146 -0
  14. data/lib/gitlab/client/branches.rb +135 -0
  15. data/lib/gitlab/client/broadcast_messages.rb +75 -0
  16. data/lib/gitlab/client/build_variables.rb +135 -0
  17. data/lib/gitlab/client/builds.rb +108 -0
  18. data/lib/gitlab/client/commits.rb +216 -0
  19. data/lib/gitlab/client/container_registry.rb +85 -0
  20. data/lib/gitlab/client/deployments.rb +34 -0
  21. data/lib/gitlab/client/environments.rb +89 -0
  22. data/lib/gitlab/client/epic_issues.rb +23 -0
  23. data/lib/gitlab/client/epics.rb +73 -0
  24. data/lib/gitlab/client/events.rb +60 -0
  25. data/lib/gitlab/client/features.rb +48 -0
  26. data/lib/gitlab/client/group_badges.rb +88 -0
  27. data/lib/gitlab/client/group_boards.rb +141 -0
  28. data/lib/gitlab/client/group_labels.rb +88 -0
  29. data/lib/gitlab/client/group_milestones.rb +94 -0
  30. data/lib/gitlab/client/groups.rb +358 -0
  31. data/lib/gitlab/client/issue_links.rb +48 -0
  32. data/lib/gitlab/client/issues.rb +231 -0
  33. data/lib/gitlab/client/jobs.rb +250 -0
  34. data/lib/gitlab/client/keys.rb +29 -0
  35. data/lib/gitlab/client/labels.rb +88 -0
  36. data/lib/gitlab/client/lint.rb +19 -0
  37. data/lib/gitlab/client/markdown.rb +23 -0
  38. data/lib/gitlab/client/merge_request_approvals.rb +265 -0
  39. data/lib/gitlab/client/merge_requests.rb +386 -0
  40. data/lib/gitlab/client/milestones.rb +106 -0
  41. data/lib/gitlab/client/namespaces.rb +22 -0
  42. data/lib/gitlab/client/notes.rb +313 -0
  43. data/lib/gitlab/client/packages.rb +95 -0
  44. data/lib/gitlab/client/pipeline_schedules.rb +147 -0
  45. data/lib/gitlab/client/pipeline_triggers.rb +103 -0
  46. data/lib/gitlab/client/pipelines.rb +105 -0
  47. data/lib/gitlab/client/project_badges.rb +85 -0
  48. data/lib/gitlab/client/project_clusters.rb +83 -0
  49. data/lib/gitlab/client/project_release_links.rb +76 -0
  50. data/lib/gitlab/client/project_releases.rb +79 -0
  51. data/lib/gitlab/client/projects.rb +708 -0
  52. data/lib/gitlab/client/protected_tags.rb +59 -0
  53. data/lib/gitlab/client/remote_mirrors.rb +51 -0
  54. data/lib/gitlab/client/repositories.rb +113 -0
  55. data/lib/gitlab/client/repository_files.rb +131 -0
  56. data/lib/gitlab/client/repository_submodules.rb +27 -0
  57. data/lib/gitlab/client/resource_label_events.rb +82 -0
  58. data/lib/gitlab/client/resource_state_events.rb +57 -0
  59. data/lib/gitlab/client/runners.rb +211 -0
  60. data/lib/gitlab/client/search.rb +66 -0
  61. data/lib/gitlab/client/services.rb +53 -0
  62. data/lib/gitlab/client/sidekiq.rb +39 -0
  63. data/lib/gitlab/client/snippets.rb +95 -0
  64. data/lib/gitlab/client/system_hooks.rb +64 -0
  65. data/lib/gitlab/client/tags.rb +97 -0
  66. data/lib/gitlab/client/templates.rb +100 -0
  67. data/lib/gitlab/client/todos.rb +46 -0
  68. data/lib/gitlab/client/user_snippets.rb +114 -0
  69. data/lib/gitlab/client/users.rb +397 -0
  70. data/lib/gitlab/client/versions.rb +18 -0
  71. data/lib/gitlab/client/wikis.rb +79 -0
  72. data/lib/gitlab/client.rb +95 -0
  73. data/lib/gitlab/configuration.rb +57 -0
  74. data/lib/gitlab/error.rb +170 -0
  75. data/lib/gitlab/file_response.rb +48 -0
  76. data/lib/gitlab/help.rb +94 -0
  77. data/lib/gitlab/objectified_hash.rb +51 -0
  78. data/lib/gitlab/page_links.rb +35 -0
  79. data/lib/gitlab/paginated_response.rb +110 -0
  80. data/lib/gitlab/request.rb +109 -0
  81. data/lib/gitlab/shell.rb +83 -0
  82. data/lib/gitlab/shell_history.rb +57 -0
  83. data/lib/gitlab/version.rb +5 -0
  84. data/lib/gitlab.rb +56 -0
  85. metadata +204 -0
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Gitlab::Client
4
+ # Defines methods related to wikis.
5
+ # @see https://docs.gitlab.com/ce/api/wikis.html
6
+ module Wikis
7
+ # Get all wiki pages for a given project.
8
+ #
9
+ # @example
10
+ # Gitlab.wikis(3)
11
+ # Gitlab.wikis(3, {with_content: 'Some wiki content'})
12
+ #
13
+ # @param [Integer, String] project The ID or name of a project.
14
+ # @param [Hash] options A customizable set of options.
15
+ # @option options [String] with_content(optional) Include pages content
16
+ # @return [Array<Gitlab::ObjectifiedHash>]
17
+ def wikis(project, options = {})
18
+ get("/projects/#{url_encode project}/wikis", query: options)
19
+ end
20
+
21
+ # Get a wiki page for a given project.
22
+ #
23
+ # @example
24
+ # Gitlab.wiki(3, 'home')
25
+ #
26
+ # @param [Integer, String] project The ID or name of a project.
27
+ # @param [String] slug The slug (a unique string) of the wiki page
28
+ # @return [Gitlab::ObjectifiedHash]
29
+ def wiki(project, slug)
30
+ get("/projects/#{url_encode project}/wikis/#{slug}")
31
+ end
32
+
33
+ # Creates a new wiki page for the given repository with the given title, slug, and content.
34
+ #
35
+ # @example
36
+ # Gitlab.create_wiki(3, 'Some Title', 'Some Content')
37
+ # Gitlab.create_wiki(3, 'Some Title', 'Some Content', { format: 'rdoc' })
38
+ #
39
+ # @param [Integer, String] project The ID or name of a project.
40
+ # @param [String] content The content of the wiki page.
41
+ # @param [String] title The title of the wiki page.
42
+ # @param [Hash] options A customizable set of options.
43
+ # @option options [String] format (optional) The format of the wiki page. Available formats are: markdown (default), rdoc, and asciidoc.
44
+ # @return [Gitlab::ObjectifiedHash] Information about created wiki page.
45
+ def create_wiki(project, title, content, options = {})
46
+ body = { content: content, title: title }.merge(options)
47
+ post("/projects/#{url_encode project}/wikis", body: body)
48
+ end
49
+
50
+ # Updates an existing wiki page. At least one parameter is required to update the wiki page.
51
+ #
52
+ # @example
53
+ # Gitlab.update_wiki(6, 'home', { title: 'New title' })
54
+ # Gitlab.update_wiki(6, 'home', { title: 'New title', content: 'New Message', format: 'rdoc' })
55
+ #
56
+ # @param [Integer, String] project The ID or name of a project.
57
+ # @param [String] slug The slug (a unique string) of the wiki page.
58
+ # @param [Hash] options A customizable set of options.
59
+ # @option options [String] content The content of the wiki page.
60
+ # @option options [String] title The title of the wiki page.
61
+ # @option options [String] format (optional) The format of the wiki page. Available formats are: markdown (default), rdoc, and asciidoc.
62
+ # @return [Gitlab::ObjectifiedHash] Information about updated wiki page.
63
+ def update_wiki(project, slug, options = {})
64
+ put("/projects/#{url_encode project}/wikis/#{slug}", body: options)
65
+ end
66
+
67
+ # Deletes a wiki page with a given slug.
68
+ #
69
+ # @example
70
+ # Gitlab.delete_wiki(42, 'foo')
71
+ #
72
+ # @param [Integer, String] project The ID or name of a project.
73
+ # @param [String] slug The slug (a unique string) of the wiki page.
74
+ # @return [Gitlab::ObjectifiedHash] An empty objectified hash
75
+ def delete_wiki(project, slug)
76
+ delete("/projects/#{url_encode project}/wikis/#{slug}")
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ # Wrapper for the Gitlab REST API.
5
+ class Client < API
6
+ Dir[File.expand_path('client/*.rb', __dir__)].each { |f| require f }
7
+
8
+ # Please keep in alphabetical order
9
+ include AccessRequests
10
+ include ApplicationSettings
11
+ include Avatar
12
+ include AwardEmojis
13
+ include Boards
14
+ include Branches
15
+ include BroadcastMessages
16
+ include BuildVariables
17
+ include Builds
18
+ include Commits
19
+ include ContainerRegistry
20
+ include Deployments
21
+ include Environments
22
+ include EpicIssues
23
+ include Epics
24
+ include Events
25
+ include Features
26
+ include GroupBadges
27
+ include GroupBoards
28
+ include GroupLabels
29
+ include GroupMilestones
30
+ include Groups
31
+ include IssueLinks
32
+ include Issues
33
+ include Jobs
34
+ include Keys
35
+ include Labels
36
+ include Lint
37
+ include Markdown
38
+ include MergeRequestApprovals
39
+ include MergeRequests
40
+ include Milestones
41
+ include Namespaces
42
+ include Notes
43
+ include PipelineSchedules
44
+ include PipelineTriggers
45
+ include Pipelines
46
+ include ProjectBadges
47
+ include ProjectClusters
48
+ include ProjectReleaseLinks
49
+ include ProjectReleases
50
+ include Projects
51
+ include ProtectedTags
52
+ include RemoteMirrors
53
+ include Repositories
54
+ include RepositoryFiles
55
+ include RepositorySubmodules
56
+ include ResourceLabelEvents
57
+ include ResourceStateEvents
58
+ include Runners
59
+ include Search
60
+ include Services
61
+ include Sidekiq
62
+ include Snippets
63
+ include SystemHooks
64
+ include Tags
65
+ include Templates
66
+ include Todos
67
+ include Users
68
+ include UserSnippets
69
+ include Versions
70
+ include Wikis
71
+
72
+ # Text representation of the client, masking private token.
73
+ #
74
+ # @return [String]
75
+ def inspect
76
+ inspected = super
77
+ inspected.sub! @private_token, only_show_last_four_chars(@private_token) if @private_token
78
+ inspected
79
+ end
80
+
81
+ # Utility method for URL encoding of a string.
82
+ # Copied from https://ruby-doc.org/stdlib-2.7.0/libdoc/erb/rdoc/ERB/Util.html
83
+ #
84
+ # @return [String]
85
+ def url_encode(url)
86
+ url.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m| sprintf('%%%02X', m.unpack1('C')) } # rubocop:disable Style/FormatString
87
+ end
88
+
89
+ private
90
+
91
+ def only_show_last_four_chars(token)
92
+ "#{'*' * (token.size - 4)}#{token[-4..]}"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gitlab/cli_helpers'
4
+ module Gitlab
5
+ # Defines constants and methods related to configuration.
6
+ module Configuration
7
+ # An array of valid keys in the options hash when configuring a Gitlab::API.
8
+ VALID_OPTIONS_KEYS = %i[endpoint private_token user_agent sudo httparty].freeze
9
+
10
+ # The user agent that will be sent to the API endpoint if none is set.
11
+ DEFAULT_USER_AGENT = "Gitlab Ruby Gem #{Gitlab::VERSION}"
12
+
13
+ # @private
14
+ attr_accessor(*VALID_OPTIONS_KEYS)
15
+ # @private
16
+ alias auth_token= private_token=
17
+
18
+ # Sets all configuration options to their default values
19
+ # when this module is extended.
20
+ def self.extended(base)
21
+ base.reset
22
+ end
23
+
24
+ # Convenience method to allow configuration options to be set in a block.
25
+ def configure
26
+ yield self
27
+ end
28
+
29
+ # Creates a hash of options and their values.
30
+ def options
31
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
32
+ option.merge!(key => send(key))
33
+ end
34
+ end
35
+
36
+ # Resets all configuration options to the defaults.
37
+ def reset
38
+ self.endpoint = ENV['GITLAB_API_ENDPOINT'] || ENV['CI_API_V4_URL']
39
+ self.private_token = ENV['GITLAB_API_PRIVATE_TOKEN'] || ENV['GITLAB_API_AUTH_TOKEN']
40
+ self.httparty = get_httparty_config(ENV['GITLAB_API_HTTPARTY_OPTIONS'])
41
+ self.sudo = nil
42
+ self.user_agent = DEFAULT_USER_AGENT
43
+ end
44
+
45
+ private
46
+
47
+ # Allows HTTParty config to be specified in ENV using YAML hash.
48
+ def get_httparty_config(options)
49
+ return if options.nil?
50
+
51
+ httparty = Gitlab::CLI::Helpers.yaml_load(options)
52
+ raise ArgumentError, 'HTTParty config should be a Hash.' unless httparty.is_a? Hash
53
+
54
+ Gitlab::CLI::Helpers.symbolize_keys httparty
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module Error
5
+ # Custom error class for rescuing from all Gitlab errors.
6
+ class Error < StandardError; end
7
+
8
+ # Raised when API endpoint credentials not configured.
9
+ class MissingCredentials < Error; end
10
+
11
+ # Raised when impossible to parse response body.
12
+ class Parsing < Error; end
13
+
14
+ # Custom error class for rescuing from HTTP response errors.
15
+ class ResponseError < Error
16
+ POSSIBLE_MESSAGE_KEYS = %i[message error_description error].freeze
17
+
18
+ def initialize(response)
19
+ @response = response
20
+ super(build_error_message)
21
+ end
22
+
23
+ # Status code returned in the HTTP response.
24
+ #
25
+ # @return [Integer]
26
+ def response_status
27
+ @response.code
28
+ end
29
+
30
+ # Body content returned in the HTTP response
31
+ #
32
+ # @return [String]
33
+ def response_message
34
+ @response.parsed_response.message
35
+ end
36
+
37
+ # Additional error context returned by some API endpoints
38
+ #
39
+ # @return [String]
40
+ def error_code
41
+ if @response.respond_to?(:error_code)
42
+ @response.error_code
43
+ else
44
+ ''
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # Human friendly message.
51
+ #
52
+ # @return [String]
53
+ def build_error_message
54
+ parsed_response = classified_response
55
+ message = check_error_keys(parsed_response)
56
+ "Server responded with code #{@response.code}, message: " \
57
+ "#{handle_message(message)}. " \
58
+ "Request URI: #{@response.request.base_uri}#{@response.request.path}"
59
+ end
60
+
61
+ # Error keys vary across the API, find the first key that the parsed_response
62
+ # object responds to and return that, otherwise return the original.
63
+ def check_error_keys(resp)
64
+ key = POSSIBLE_MESSAGE_KEYS.find { |k| resp.respond_to?(k) }
65
+ key ? resp.send(key) : resp
66
+ end
67
+
68
+ # Parse the body based on the classification of the body content type
69
+ #
70
+ # @return parsed response
71
+ def classified_response
72
+ if @response.respond_to?('headers')
73
+ @response.headers['content-type'] == 'text/plain' ? { message: @response.to_s } : @response.parsed_response
74
+ else
75
+ @response.parsed_response
76
+ end
77
+ rescue Gitlab::Error::Parsing
78
+ # Return stringified response when receiving a
79
+ # parsing error to avoid obfuscation of the
80
+ # api error.
81
+ #
82
+ # note: The Gitlab API does not always return valid
83
+ # JSON when there are errors.
84
+ @response.to_s
85
+ end
86
+
87
+ # Handle error response message in case of nested hashes
88
+ def handle_message(message)
89
+ case message
90
+ when Gitlab::ObjectifiedHash
91
+ message.to_h.sort.map do |key, val|
92
+ "'#{key}' #{(val.is_a?(Hash) ? val.sort.map { |k, v| "(#{k}: #{v.join(' ')})" } : [val].flatten).join(' ')}"
93
+ end.join(', ')
94
+ when Array
95
+ message.join(' ')
96
+ else
97
+ message
98
+ end
99
+ end
100
+ end
101
+
102
+ # Raised when API endpoint returns the HTTP status code 400.
103
+ class BadRequest < ResponseError; end
104
+
105
+ # Raised when API endpoint returns the HTTP status code 401.
106
+ class Unauthorized < ResponseError; end
107
+
108
+ # Raised when API endpoint returns the HTTP status code 403.
109
+ class Forbidden < ResponseError; end
110
+
111
+ # Raised when API endpoint returns the HTTP status code 404.
112
+ class NotFound < ResponseError; end
113
+
114
+ # Raised when API endpoint returns the HTTP status code 405.
115
+ class MethodNotAllowed < ResponseError; end
116
+
117
+ # Raised when API endpoint returns the HTTP status code 406.
118
+ class NotAcceptable < ResponseError; end
119
+
120
+ # Raised when API endpoint returns the HTTP status code 409.
121
+ class Conflict < ResponseError; end
122
+
123
+ # Raised when API endpoint returns the HTTP status code 422.
124
+ class Unprocessable < ResponseError; end
125
+
126
+ # Raised when API endpoint returns the HTTP status code 429.
127
+ class TooManyRequests < ResponseError; end
128
+
129
+ # Raised when API endpoint returns the HTTP status code 500.
130
+ class InternalServerError < ResponseError; end
131
+
132
+ # Raised when API endpoint returns the HTTP status code 502.
133
+ class BadGateway < ResponseError; end
134
+
135
+ # Raised when API endpoint returns the HTTP status code 503.
136
+ class ServiceUnavailable < ResponseError; end
137
+
138
+ # Raised when API endpoint returns the HTTP status code 522.
139
+ class ConnectionTimedOut < ResponseError; end
140
+
141
+ # HTTP status codes mapped to error classes.
142
+ STATUS_MAPPINGS = {
143
+ 400 => BadRequest,
144
+ 401 => Unauthorized,
145
+ 403 => Forbidden,
146
+ 404 => NotFound,
147
+ 405 => MethodNotAllowed,
148
+ 406 => NotAcceptable,
149
+ 409 => Conflict,
150
+ 422 => Unprocessable,
151
+ 429 => TooManyRequests,
152
+ 500 => InternalServerError,
153
+ 502 => BadGateway,
154
+ 503 => ServiceUnavailable,
155
+ 522 => ConnectionTimedOut
156
+ }.freeze
157
+
158
+ # Returns error class that should be raised for this response. Returns nil
159
+ # if the response status code is not 4xx or 5xx.
160
+ #
161
+ # @param [HTTParty::Response] response The response object.
162
+ # @return [Class<Error::ResponseError>, nil]
163
+ def self.klass(response)
164
+ error_klass = STATUS_MAPPINGS[response.code]
165
+ return error_klass if error_klass
166
+
167
+ ResponseError if response.server_error? || response.client_error?
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ # Wrapper class of file response.
5
+ class FileResponse
6
+ HEADER_CONTENT_DISPOSITION = 'Content-Disposition'
7
+
8
+ attr_reader :filename
9
+
10
+ def initialize(file)
11
+ @file = file
12
+ end
13
+
14
+ # @return [bool] Always false
15
+ def empty?
16
+ false
17
+ end
18
+
19
+ # @return [Hash] A hash consisting of filename and io object
20
+ def to_hash
21
+ { filename: @filename, data: @file }
22
+ end
23
+ alias to_h to_hash
24
+
25
+ # @return [String] Formatted string with the class name, object id and filename.
26
+ def inspect
27
+ "#<#{self.class}:#{object_id} {filename: #{filename.inspect}}>"
28
+ end
29
+
30
+ def method_missing(name, *args, &block)
31
+ if @file.respond_to?(name)
32
+ @file.send(name, *args, &block)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def respond_to_missing?(method_name, include_private = false)
39
+ super || @file.respond_to?(method_name, include_private)
40
+ end
41
+
42
+ # Parse filename from the 'Content Disposition' header.
43
+ def parse_headers!(headers)
44
+ @filename = headers[HEADER_CONTENT_DISPOSITION].split('filename=')[1]
45
+ @filename = @filename[1...-1] if @filename[0] == '"' # Unquote filenames
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gitlab'
4
+ require 'gitlab/cli_helpers'
5
+
6
+ module Gitlab::Help
7
+ extend Gitlab::CLI::Helpers
8
+
9
+ class << self
10
+ # Returns the (modified) help from the 'ri' command or returns an error.
11
+ #
12
+ # @return [String]
13
+ def get_help(cmd)
14
+ cmd_namespace = namespace cmd
15
+
16
+ if cmd_namespace
17
+ ri_output = `#{ri_cmd} -T #{cmd_namespace} 2>&1`.chomp
18
+
19
+ if $CHILD_STATUS == 0
20
+ change_help_output! cmd, ri_output
21
+ yield ri_output if block_given?
22
+
23
+ ri_output
24
+ else
25
+ "Ri docs not found for #{cmd}, please install the docs to use 'help'."
26
+ end
27
+ else
28
+ "Unknown command: #{cmd}."
29
+ end
30
+ end
31
+
32
+ # Finds the location of 'ri' on a system.
33
+ #
34
+ # @return [String]
35
+ def ri_cmd
36
+ which_ri = `which ri`.chomp
37
+ raise "'ri' tool not found in $PATH. Please install it to use the help." if which_ri.empty?
38
+
39
+ which_ri
40
+ end
41
+
42
+ # A hash map that contains help topics (Branches, Groups, etc.)
43
+ # and a list of commands that are defined under a topic (create_branch,
44
+ # branches, protect_branch, etc.).
45
+ #
46
+ # @return [Hash<Array>]
47
+ def help_map
48
+ @help_map ||=
49
+ actions.each_with_object({}) do |action, hsh|
50
+ key = client.method(action)
51
+ .owner.to_s.gsub(/Gitlab::(?:Client::)?/, '')
52
+ hsh[key] ||= []
53
+ hsh[key] << action.to_s
54
+ end
55
+ end
56
+
57
+ # Table with available commands.
58
+ #
59
+ # @return [Terminal::Table]
60
+ def actions_table(topic = nil)
61
+ rows = topic ? help_map[topic] : help_map.keys
62
+ table do |t|
63
+ t.title = topic || 'Help Topics'
64
+
65
+ # add_row expects an array and we have strings hence the map.
66
+ rows.sort.map { |r| [r] }.each_with_index do |row, index|
67
+ t.add_row row
68
+ t.add_separator unless rows.size - 1 == index
69
+ end
70
+ end
71
+ end
72
+
73
+ # Returns full namespace of a command (e.g. Gitlab::Client::Branches.cmd)
74
+ def namespace(cmd)
75
+ method_owners.select { |method| method[:name] == cmd }
76
+ .map { |method| "#{method[:owner]}.#{method[:name]}" }
77
+ .shift
78
+ end
79
+
80
+ # Massage output from 'ri'.
81
+ def change_help_output!(cmd, output_str)
82
+ output_str = +output_str
83
+ output_str.gsub!(/#{cmd}(\(.*?\))/m, "#{cmd}\\1")
84
+ output_str.gsub!(/,\s*/, ', ')
85
+
86
+ # Ensure @option descriptions are on a single line
87
+ output_str.gsub!(/\n\[/, " \[")
88
+ output_str.gsub!(/\s(@)/, "\n@")
89
+ output_str.gsub!(/(\])\n(:)/, '\\1 \\2')
90
+ output_str.gsub!(/(:.*)(\n)(.*\.)/, '\\1 \\3')
91
+ output_str.gsub!(/\{(.+)\}/, '"{\\1}"')
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ # Converts hashes to the objects.
5
+ class ObjectifiedHash
6
+ # Creates a new ObjectifiedHash object.
7
+ def initialize(hash)
8
+ @hash = hash
9
+ @data = hash.each_with_object({}) do |(key, value), data|
10
+ value = self.class.new(value) if value.is_a? Hash
11
+ value = value.map { |v| v.is_a?(Hash) ? self.class.new(v) : v } if value.is_a? Array
12
+ data[key.to_s] = value
13
+ end
14
+ end
15
+
16
+ # @return [Hash] The original hash.
17
+ def to_hash
18
+ hash
19
+ end
20
+ alias to_h to_hash
21
+
22
+ # @return [String] Formatted string with the class name, object id and original hash.
23
+ def inspect
24
+ "#<#{self.class}:#{object_id} {hash: #{hash.inspect}}"
25
+ end
26
+
27
+ def [](key)
28
+ data[key]
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :hash, :data
34
+
35
+ # Respond to messages for which `self.data` has a key
36
+ def method_missing(method_name, *args, &block)
37
+ if data.key?(method_name.to_s)
38
+ data[method_name.to_s]
39
+ elsif data.respond_to?(method_name)
40
+ warn 'WARNING: Please convert ObjectifiedHash object to hash before calling Hash methods on it.'
41
+ data.send(method_name, *args, &block)
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def respond_to_missing?(method_name, include_private = false)
48
+ hash.keys.map(&:to_sym).include?(method_name.to_sym) || super
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ # Parses link header.
5
+ #
6
+ # @private
7
+ class PageLinks
8
+ HEADER_LINK = 'Link'
9
+ DELIM_LINKS = ','
10
+ LINK_REGEX = /<([^>]+)>; rel="([^"]+)"/.freeze
11
+ METAS = %w[last next first prev].freeze
12
+
13
+ attr_accessor(*METAS)
14
+
15
+ def initialize(headers)
16
+ link_header = headers[HEADER_LINK]
17
+
18
+ extract_links(link_header) if link_header && link_header =~ /(next|first|last|prev)/
19
+ end
20
+
21
+ private
22
+
23
+ def extract_links(header)
24
+ header.split(DELIM_LINKS).each do |link|
25
+ LINK_REGEX.match(link.strip) do |match|
26
+ url = match[1]
27
+ meta = match[2]
28
+ next if !url || !meta || METAS.index(meta).nil?
29
+
30
+ send("#{meta}=", url)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end