diffend 0.2.16 → 0.2.24

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.
@@ -7,9 +7,19 @@ module Diffend
7
7
  BaseError = Class.new(StandardError)
8
8
  # Raised when we couldn't find a valid configuration file
9
9
  MissingConfigurationFile = Class.new(BaseError)
10
- # When unsupported response returned from the endpoint
11
- UnsupportedResponse = Class.new(BaseError)
12
- # When unsupported action returned from the endpoint
13
- UnsupportedAction = Class.new(BaseError)
10
+ # Raised when configuration file is empty
11
+ EmptyConfigurationFile = Class.new(BaseError)
12
+ # Raised when configuration file is malformed
13
+ MalformedConfigurationFile = Class.new(BaseError)
14
+ # Raised when project_id is missing in configuration file
15
+ ProjectIdMissingInConfigurationFile = Class.new(BaseError)
16
+ # Raised when shareable_id is missing in configuration file
17
+ ShareableIdMissingInConfigurationFile = Class.new(BaseError)
18
+ # Raised when shareable_key is missing in configuration file
19
+ ShareableKeyMissingInConfigurationFile = Class.new(BaseError)
20
+ # Raised when build_path is missing in configuration file
21
+ BuildPathMissingInConfigurationFile = Class.new(BaseError)
22
+ # Raised when server-side error occurs
23
+ RequestServerError = Class.new(BaseError)
14
24
  end
15
25
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Diffend
6
+ module HandleErrors
7
+ # Module responsible for building exception payload
8
+ module BuildExceptionPayload
9
+ class << self
10
+ # Build exception payload
11
+ #
12
+ # @param exception [Exception, NilClass] expection that was raised
13
+ # @param payload [Hash] with versions to check
14
+ #
15
+ # @return [Hash]
16
+ def call(exception, payload)
17
+ {
18
+ request_id: SecureRandom.uuid,
19
+ payload: payload,
20
+ exception: {
21
+ class: exception&.class,
22
+ message: exception&.message,
23
+ backtrace: exception&.backtrace
24
+ }
25
+ }.freeze
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diffend
4
+ module HandleErrors
5
+ # Module responsible for displaying exception payload to stdout
6
+ module DisplayToStdout
7
+ class << self
8
+ # Display exception payload to stdout
9
+ #
10
+ # @param exception_payload [Hash]
11
+ def call(exception_payload)
12
+ puts exception_payload.to_json
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diffend
4
+ module HandleErrors
5
+ module Messages
6
+ PAYLOAD_DUMP = '^^^ Above is the dump of your request ^^^'
7
+ UNHANDLED_EXCEPTION = <<~MSG
8
+ \nSomething went really wrong. We recorded this incident in our system and will review it.\n
9
+ This is a bug, don't hesitate.\n
10
+ Create an issue at https://github.com/diffend-io/diffend-ruby/issues\n
11
+ MSG
12
+ UNSUPPORTED_RESPONSE = <<~MSG
13
+ \nAPI returned an unsupported response. We recorded this incident in our system and will review it.\n
14
+ This is a bug, don't hesitate.\n
15
+ Create an issue at https://github.com/diffend-io/diffend-ruby/issues\n
16
+ MSG
17
+ UNSUPPORTED_VERDICT = <<~MSG
18
+ \nAPI returned an unsupported verdict. We recorded this incident in our system and will review it.\n
19
+ This is a bug, don't hesitate.\n
20
+ Create an issue at https://github.com/diffend-io/diffend-ruby/issues\n
21
+ MSG
22
+ REQUEST_ERROR = <<~MSG
23
+ \nWe were unable to process your request at this time. We recorded this incident in our system and will review it.\n
24
+ If you think that this is a bug, don't hesitate.\n
25
+ Create an issue at https://github.com/diffend-io/diffend-ruby/issues\n
26
+ MSG
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diffend
4
+ module HandleErrors
5
+ # Module responsible for reporting errors to diffend
6
+ module Report
7
+ class << self
8
+ # Execute request to Diffend
9
+ #
10
+ # @param exception [Exception] expection that was raised
11
+ # @param payload [Hash] with versions to check
12
+ # @param config [OpenStruct] Diffend config
13
+ # @param message [Symbol] message that we want to display
14
+ # @param report [Boolean] if true we will report the issue to diffend
15
+ #
16
+ # @return [Net::HTTPResponse] response from Diffend
17
+ def call(config:, message:, exception: nil, payload: {}, report: false)
18
+ exception_payload = prepare_exception_payload(exception, payload)
19
+
20
+ Bundler.ui.error(Diffend::HandleErrors::Messages::PAYLOAD_DUMP)
21
+ Bundler.ui.error(Diffend::HandleErrors::Messages.const_get(message.to_s.upcase))
22
+
23
+ if report
24
+ Diffend::Request.call(
25
+ config,
26
+ errors_url(config.project_id),
27
+ exception_payload
28
+ )
29
+ end
30
+
31
+ exit 1
32
+ end
33
+
34
+ # Prepare exception payload and display it to stdout
35
+ #
36
+ # @param exception [Exception] expection that was raised
37
+ # @param payload [Hash] with versions to check
38
+ #
39
+ # @return [Hash]
40
+ def prepare_exception_payload(exception, payload)
41
+ Diffend::HandleErrors::BuildExceptionPayload
42
+ .call(exception, payload)
43
+ .tap(&Diffend::HandleErrors::DisplayToStdout.method(:call))
44
+ end
45
+
46
+ # Provides diffend errors endpoint url
47
+ #
48
+ # @param project_id [String] diffend project_id
49
+ #
50
+ # @return [String] diffend endpoint
51
+ def errors_url(project_id)
52
+ return ENV['DIFFEND_ERROR_URL'] if ENV.key?('DIFFEND_ERROR_URL')
53
+
54
+ "https://my.diffend.io/api/projects/#{project_id}/errors"
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'openssl'
5
+ require 'json'
6
+
7
+ module Diffend
8
+ # Module responsible for doing request to Diffend
9
+ module Request
10
+ # Message displayed when connection issue occured and we will retry
11
+ CONNECTION_MESSAGE = 'We experienced a connection issue, retrying...'
12
+ # List of connection exceptions
13
+ CONNECTION_EXCEPTIONS = [
14
+ Errno::ECONNRESET,
15
+ Errno::ENETUNREACH,
16
+ Errno::EHOSTUNREACH,
17
+ Errno::ECONNREFUSED
18
+ ].freeze
19
+ # Message displayed when timeout occured and we will retry
20
+ TIMEOUT_MESSAGE = 'We experienced a connection issue, retrying...'
21
+ # List of timeout exceptions
22
+ TIMEOUT_EXCEPTIONS = [
23
+ Net::OpenTimeout,
24
+ Net::ReadTimeout
25
+ ].freeze
26
+ # Message displayed when server issue occured and we will retry
27
+ SERVER_ERROR_MESSAGE = 'We experienced a server-side issue, retrying...'
28
+ # List of server issues
29
+ #
30
+ # 500 - Internal Server Error
31
+ # 502 - Bad Gateway
32
+ # 503 - Service Unavailable
33
+ # 504 - Gateway Timeout
34
+ SERVER_ERRORS = [500, 502, 503, 504].freeze
35
+ # Number of retries
36
+ RETRIES = 3
37
+ # Request headers
38
+ HEADERS = { 'Content-Type': 'application/json' }.freeze
39
+
40
+ private_constant :HEADERS
41
+
42
+ class << self
43
+ # Execute request
44
+ #
45
+ # @param config [OpenStruct] diffend config
46
+ # @param endpoint_url [String]
47
+ # @param payload [Hash]
48
+ #
49
+ # @return [Net::HTTPResponse] response from Diffend
50
+ def call(config, endpoint_url, payload)
51
+ retry_count ||= -1
52
+
53
+ build_http(endpoint_url) do |http, uri|
54
+ response = http.request(build_request(uri, config, payload))
55
+
56
+ if SERVER_ERRORS.include?(response.code.to_i)
57
+ raise Diffend::Errors::RequestServerError, response.code.to_i
58
+ end
59
+
60
+ response
61
+ end
62
+ rescue Diffend::Errors::RequestServerError => e
63
+ retry_count += 1
64
+
65
+ retry if handle_retry(SERVER_ERROR_MESSAGE, retry_count)
66
+
67
+ Diffend::HandleErrors::Report.call(
68
+ exception: e,
69
+ payload: payload,
70
+ config: config,
71
+ message: :request_error
72
+ )
73
+ rescue *CONNECTION_EXCEPTIONS => e
74
+ retry_count += 1
75
+
76
+ retry if handle_retry(CONNECTION_MESSAGE, retry_count)
77
+
78
+ Diffend::HandleErrors::Report.call(
79
+ exception: e,
80
+ payload: payload,
81
+ config: config,
82
+ message: :request_error
83
+ )
84
+ rescue *TIMEOUT_EXCEPTIONS => e
85
+ retry_count += 1
86
+
87
+ retry if handle_retry(TIMEOUT_MESSAGE, retry_count)
88
+
89
+ Diffend::HandleErrors::Report.call(
90
+ exception: e,
91
+ payload: payload,
92
+ config: config,
93
+ message: :request_error
94
+ )
95
+ end
96
+
97
+ # Handle retry
98
+ #
99
+ # @param message [String] message we want to display
100
+ # @param retry_count [Integer]
101
+ def handle_retry(message, retry_count)
102
+ return false if retry_count == RETRIES
103
+
104
+ Bundler.ui.error(message)
105
+ sleep(exponential_backoff(retry_count))
106
+
107
+ retry_count < RETRIES
108
+ end
109
+
110
+ # Builds http connection object
111
+ #
112
+ # @param url [String] command endpoint url
113
+ def build_http(url)
114
+ uri = URI(url)
115
+
116
+ Net::HTTP.start(
117
+ uri.host,
118
+ uri.port,
119
+ use_ssl: uri.scheme == 'https',
120
+ verify_mode: OpenSSL::SSL::VERIFY_NONE,
121
+ open_timeout: 5,
122
+ read_timeout: 5
123
+ ) { |http| yield(http, uri) }
124
+ end
125
+
126
+ # Build http post request and assigns headers and payload
127
+ #
128
+ # @param uri [URI::HTTPS]
129
+ # @param config [OpenStruct] Diffend config
130
+ # @param payload [Hash] with versions to check
131
+ #
132
+ # @return [Net::HTTP::Post]
133
+ def build_request(uri, config, payload)
134
+ Net::HTTP::Post
135
+ .new(uri.request_uri, HEADERS)
136
+ .tap { |request| assign_auth(request, config) }
137
+ .tap { |request| assign_payload(request, payload) }
138
+ end
139
+
140
+ # Assigns basic authorization if provided in the config
141
+ #
142
+ # @param request [Net::HTTP::Post] prepared http post
143
+ # @param config [OpenStruct] Diffend config
144
+ def assign_auth(request, config)
145
+ return unless config
146
+ return unless config.shareable_id
147
+ return unless config.shareable_key
148
+
149
+ request.basic_auth(config.shareable_id, config.shareable_key)
150
+ end
151
+
152
+ # Assigns payload as json
153
+ #
154
+ # @param request [Net::HTTP::Post] prepared http post
155
+ # @param payload [Hash] with versions to check
156
+ def assign_payload(request, payload)
157
+ request.body = JSON.dump(payload: payload)
158
+ end
159
+
160
+ def exponential_backoff(retry_count)
161
+ 2**(retry_count + 1)
162
+ end
163
+ end
164
+ end
165
+ end
@@ -7,23 +7,33 @@ module Diffend
7
7
  # Build verdict
8
8
  #
9
9
  # @param command [String] either install or update
10
+ # @param config [OpenStruct] diffend config
10
11
  # @param definition [Bundler::Definition] definition for your source
11
- def call(command, definition)
12
+ def call(command, config, definition)
12
13
  Versions::Remote
13
- .call(command, definition)
14
- .tap { |response| build_message(command, response) }
14
+ .call(command, config, definition)
15
+ .tap { |response| build_message(command, config, response) }
15
16
  end
16
17
 
17
- def build_message(command, response)
18
+ # @param command [String] either install or update
19
+ # @param config [OpenStruct] diffend config
20
+ # @param response [Hash] response from diffend API
21
+ def build_message(command, config, response)
18
22
  if response.key?('error')
19
23
  build_error(response)
20
24
  elsif response.key?('action')
21
- build_verdict(command, response)
25
+ build_verdict(command, config, response)
22
26
  else
23
- raise UnsupportedResponse, response['action']
27
+ Diffend::HandleErrors::Report.call(
28
+ config: config,
29
+ message: :unsupported_response,
30
+ payload: response,
31
+ report: true
32
+ )
24
33
  end
25
34
  end
26
35
 
36
+ # @param response [Hash] response from diffend API
27
37
  def build_error(response)
28
38
  build_error_message(response)
29
39
  .tap(&Bundler.ui.method(:error))
@@ -31,7 +41,10 @@ module Diffend
31
41
  exit 1
32
42
  end
33
43
 
34
- def build_verdict(command, response)
44
+ # @param command [String] either install or update
45
+ # @param config [OpenStruct] diffend config
46
+ # @param response [Hash] response from diffend API
47
+ def build_verdict(command, config, response)
35
48
  case response['action']
36
49
  when 'allow'
37
50
  build_allow_message(command, response)
@@ -42,10 +55,18 @@ module Diffend
42
55
 
43
56
  exit 1
44
57
  else
45
- raise UnsupportedAction, response['action']
58
+ Diffend::HandleErrors::Report.call(
59
+ config: config,
60
+ message: :unsupported_verdict,
61
+ payload: response,
62
+ report: true
63
+ )
46
64
  end
47
65
  end
48
66
 
67
+ # @param response [Hash] response from diffend API
68
+ #
69
+ # @return [String]
49
70
  def build_error_message(response)
50
71
  <<~MSG
51
72
  \nDiffend returned an error for your request.\n
@@ -53,6 +74,10 @@ module Diffend
53
74
  MSG
54
75
  end
55
76
 
77
+ # @param command [String] either install or update
78
+ # @param response [Hash] response from diffend API
79
+ #
80
+ # @return [String]
56
81
  def build_allow_message(command, response)
57
82
  <<~MSG
58
83
  \nDiffend reported an allow verdict for #{command} command for this project.\n
@@ -61,6 +86,10 @@ module Diffend
61
86
  MSG
62
87
  end
63
88
 
89
+ # @param command [String] either install or update
90
+ # @param response [Hash] response from diffend API
91
+ #
92
+ # @return [String]
64
93
  def build_deny_message(command, response)
65
94
  <<~MSG
66
95
  \nDiffend reported a deny verdict for #{command} command for this project.\n
@@ -13,17 +13,22 @@ module Diffend
13
13
  Bundler::Source::Gemspec,
14
14
  Bundler::Source::Path
15
15
  ].freeze
16
- DEPENDENCIES = {
16
+ # List of dependency types
17
+ DEPENDENCIES_TYPES = {
17
18
  direct: 0,
18
- dep: 1
19
+ dependency: 1
19
20
  }.freeze
20
- SOURCES = {
21
+ # List of sources types
22
+ SOURCES_TYPES = {
21
23
  valid: 0,
22
24
  multiple_primary: 1
23
25
  }.freeze
24
- GEM_SOURCES = {
26
+ # List of gem sources types
27
+ GEM_SOURCES_TYPES = {
25
28
  local: 0,
26
- gemfile: 1
29
+ gemfile_source: 1,
30
+ gemfile_git: 2,
31
+ gemfile_path: 3
27
32
  }.freeze
28
33
 
29
34
  class << self
@@ -49,43 +54,45 @@ module Diffend
49
54
  def initialize(definition)
50
55
  @definition = definition
51
56
  @direct_dependencies = Hash[definition.dependencies.map { |val| [val.name, val] }]
52
- @main_source = definition.send(:sources).rubygems_sources.last
53
57
  # Support case without Gemfile.lock
54
58
  @locked_specs = @definition.locked_gems ? @definition.locked_gems.specs : []
55
59
  end
56
60
 
61
+ # Build install specification
62
+ #
63
+ # @return [Hash]
57
64
  def build_install
58
65
  hash = build_main
59
66
 
60
- @definition.requested_specs.each do |spec|
67
+ @definition.specs.each do |spec|
61
68
  next if skip?(spec.source)
62
69
 
63
70
  locked_spec = @locked_specs.find { |s| s.name == spec.name }
64
71
 
65
- spec2 = locked_spec || spec
66
-
67
- hash['dependencies'][spec2.name] = {
68
- 'platform' => build_spec_platform(spec2),
69
- 'source' => build_spec_source(spec2),
70
- 'type' => build_dependency_type(spec2.name),
71
- 'versions' => build_versions(spec2)
72
+ hash['dependencies'][spec.name] = {
73
+ 'platform' => build_spec_platform(spec, locked_spec),
74
+ 'source' => build_spec_source(spec),
75
+ 'type' => build_dependency_type(spec.name),
76
+ 'versions' => build_versions(spec, locked_spec)
72
77
  }
73
78
  end
74
79
 
75
80
  hash
76
81
  end
77
82
 
78
- # @param definition [Bundler::Definition] definition for your source
83
+ # Build update specification
84
+ #
85
+ # @return [Hash]
79
86
  def build_update
80
87
  hash = build_main
81
88
 
82
- @definition.requested_specs.each do |spec|
89
+ @definition.specs.each do |spec|
83
90
  next if skip?(spec.source)
84
91
 
85
92
  locked_spec = @locked_specs.find { |s| s.name == spec.name }
86
93
 
87
94
  hash['dependencies'][spec.name] = {
88
- 'platform' => build_spec_platform(spec),
95
+ 'platform' => build_spec_platform(spec, locked_spec),
89
96
  'source' => build_spec_source(spec),
90
97
  'type' => build_dependency_type(spec.name),
91
98
  'versions' => build_versions(spec, locked_spec)
@@ -97,6 +104,9 @@ module Diffend
97
104
 
98
105
  private
99
106
 
107
+ # Build default specification
108
+ #
109
+ # @return [Hash]
100
110
  def build_main
101
111
  {
102
112
  'dependencies' => {},
@@ -106,8 +116,18 @@ module Diffend
106
116
  }
107
117
  end
108
118
 
119
+ # Build gem versions
120
+ #
121
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
122
+ # @param locked_spec [Bundler::LazySpecification, Gem::Specification, NilClass]
123
+ #
124
+ # @return [Array<String>]
109
125
  def build_versions(spec, locked_spec = nil)
110
- locked_spec ? [locked_spec.version.to_s, spec.version.to_s] : [spec.version.to_s]
126
+ if locked_spec && locked_spec.version.to_s != spec.version.to_s
127
+ [locked_spec.version.to_s, spec.version.to_s]
128
+ else
129
+ [spec.version.to_s]
130
+ end
111
131
  end
112
132
 
113
133
  # @param specs [Array] specs that are direct dependencies
@@ -115,78 +135,162 @@ module Diffend
115
135
  #
116
136
  # @return [Boolean] dependency type
117
137
  def build_dependency_type(name)
118
- @direct_dependencies.key?(name) ? DEPENDENCIES[:direct] : DEPENDENCIES[:dep]
138
+ if @direct_dependencies.key?(name)
139
+ DEPENDENCIES_TYPES[:direct]
140
+ else
141
+ DEPENDENCIES_TYPES[:dependency]
142
+ end
143
+ end
144
+
145
+ # Build gem platform
146
+ #
147
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
148
+ # @param locked_spec [Bundler::LazySpecification, Gem::Specification, NilClass]
149
+ #
150
+ # @return [String]
151
+ def build_spec_platform(spec, locked_spec)
152
+ parse_platform(
153
+ spec.platform || locked_spec&.platform || spec.send(:generic_local_platform)
154
+ )
155
+ end
156
+
157
+ # Parse gem platform
158
+ #
159
+ # @param platform [String, Gem::Platform]
160
+ #
161
+ # @return [String]
162
+ def parse_platform(platform)
163
+ case platform
164
+ when String then platform
165
+ when Gem::Platform then platform.os
166
+ end
119
167
  end
120
168
 
121
- def build_spec_platform(spec)
122
- spec.platform || spec.send(:generic_local_platform)
169
+ # Build gem source type
170
+ #
171
+ # @param source [Bundler::Source] gem source type
172
+ #
173
+ # @return [Integer] internal gem source type
174
+ def build_spec_gem_source_type(source)
175
+ case source
176
+ when Bundler::Source::Metadata
177
+ GEM_SOURCES_TYPES[:local]
178
+ when Bundler::Source::Rubygems, Bundler::Source::Rubygems::Remote
179
+ GEM_SOURCES_TYPES[:gemfile_source]
180
+ when Bundler::Source::Git
181
+ GEM_SOURCES_TYPES[:gemfile_git]
182
+ when Bundler::Source::Path
183
+ GEM_SOURCES_TYPES[:gemfile_path]
184
+ else
185
+ raise ArgumentError, "unknown source #{source.class}"
186
+ end
123
187
  end
124
188
 
189
+ # Build gem source
190
+ #
191
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
192
+ #
193
+ # @return [Hash]
125
194
  def build_spec_source(spec)
126
- if @direct_dependencies.key?(spec.name)
127
- dep_spec = @direct_dependencies[spec.name]
195
+ source = source_for_spec(spec)
128
196
 
129
- if dep_spec.source
130
- { 'type' => GEM_SOURCES[:gemfile], 'url' => source_name_from_source(dep_spec.source) }
131
- else
132
- { 'type' => GEM_SOURCES[:gemfile], 'url' => source_name_from_source(@main_source) }
133
- end
197
+ {
198
+ 'type' => build_spec_gem_source_type(source),
199
+ 'value' => source_name_from_source(source)
200
+ }
201
+ end
202
+
203
+ # Figure out source for gem
204
+ #
205
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
206
+ #
207
+ # @return [Bundler::Source] gem source type
208
+ def source_for_spec(spec)
209
+ return spec.remote if spec.remote
210
+
211
+ case spec.source
212
+ when Bundler::Source::Rubygems
213
+ spec
214
+ .source
215
+ .send(:remote_specs)
216
+ .search(Bundler::Dependency.new(spec.name, spec.version))
217
+ .last
218
+ .remote
219
+ when Bundler::Source::Metadata, Bundler::Source::Git, Bundler::Source::Path
220
+ spec.source
134
221
  else
135
- if spec.source.respond_to?(:remotes)
136
- { 'type' => GEM_SOURCES[:gemfile], 'url' => source_name_from_source(spec.source) }
137
- else
138
- { 'type' => GEM_SOURCES[:local], 'url' => '' }
139
- end
222
+ raise ArgumentError, "unknown source #{spec.source.class}"
140
223
  end
141
224
  end
142
225
 
226
+ # Build gem source name
227
+ #
228
+ # @param source [Bundler::Source] gem source type
229
+ #
230
+ # @return [String]
143
231
  def source_name_from_source(source)
144
- source_name(source.remotes.first)
232
+ case source
233
+ when Bundler::Source::Metadata
234
+ ''
235
+ when Bundler::Source::Rubygems::Remote
236
+ source_name(source.anonymized_uri)
237
+ when Bundler::Source::Git
238
+ source.instance_variable_get(:@safe_uri)
239
+ when Bundler::Source::Path
240
+ source.path
241
+ else
242
+ raise ArgumentError, "unknown source #{source.class}"
243
+ end
145
244
  end
146
245
 
147
- def source_name(name)
148
- name.to_s[0...-1]
246
+ # @param uri [Bundler::URI]
247
+ #
248
+ # @return [String]
249
+ def source_name(uri)
250
+ uri.to_s[0...-1]
149
251
  end
150
252
 
253
+ # Build sources used in the Gemfile
254
+ #
255
+ # @return [Array<Hash>]
151
256
  def build_sources
152
257
  sources = @definition.send(:sources).rubygems_sources
153
- hash = []
258
+ hash = {}
154
259
 
155
260
  sources.each do |source|
156
- type = source.remotes.count > 1 ? SOURCES[:multiple_primary] : SOURCES[:valid]
261
+ type = build_source_type(source.remotes)
157
262
 
158
263
  source.remotes.each do |src|
159
- hash << { 'name' => source_name(src), 'type' => type }
264
+ hash[source_name(src)] = type
160
265
  end
161
266
  end
162
267
 
163
- hash
268
+ hash.map { |name, type| { 'name' => name, 'type' => type } }
269
+ end
270
+
271
+ # Build gem source type
272
+ #
273
+ # @param remotes [Array<Bundler::URI>]
274
+ #
275
+ # @return [Integer] internal source type
276
+ def build_source_type(remotes)
277
+ remotes.count > 1 ? SOURCES_TYPES[:multiple_primary] : SOURCES_TYPES[:valid]
164
278
  end
165
279
 
166
280
  # Checks if we should skip a source
167
281
  #
168
- # @param source [Bundler::Source::Git, Bundler::Source::Rubygems]
282
+ # @param source [Bundler::Source] gem source type
169
283
  #
170
284
  # @return [Boolean] true if we should skip this source, false otherwise
171
285
  def skip?(source)
172
- return true if git?(source)
173
286
  return true if me?(source)
174
287
 
175
288
  false
176
289
  end
177
290
 
178
- # Checks if it's a git source
179
- #
180
- # @param source [Bundler::Source::Git, Bundler::Source::Rubygems]
181
- #
182
- # @return [Boolean] true if it's a git source, false otherwise
183
- def git?(source)
184
- source.instance_of?(Bundler::Source::Git)
185
- end
186
-
187
291
  # Checks if it's a self source, this happens for repositories that are a gem
188
292
  #
189
- # @param source [Bundler::Source::Path,Bundler::Source::Git,Bundler::Source::Rubygems]
293
+ # @param source [Bundler::Source] gem source type
190
294
  #
191
295
  # @return [Boolean] true if it's a self source, false otherwise
192
296
  def me?(source)