cocoapods-core 0.35.0 → 0.36.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bcbca71ceead035a92e4707de3007cc2ecad1d49
4
- data.tar.gz: 0aecd4fdb9b8dea3df82280c80fddcf6a181acae
3
+ metadata.gz: d1aef836bf83585db97dc487c15d8a5aa7290886
4
+ data.tar.gz: a08f076add586a3a79f71365ae02d9b63d314ca7
5
5
  SHA512:
6
- metadata.gz: a776fb7c8ccf469f3a1c94d1b9971e33b5a3b6a2789f4a7e15a318d2d532294ac80651d42fc4a1bc7bfc31aa92965c7862e179d87479d018ead16d500e69a7de
7
- data.tar.gz: a1150d9acc6638be13f1c864508f5c6464a6299d85aab96d4b369ad8dbbdc7a32d5bed4fdaa3497515d72f0bbbcbee3156cfc13da6aa933570dbcad9f7f6f145
6
+ metadata.gz: b8b4639f30e85fd314a5fd93c2782dc2a7d125916760b4af0db485cead247b5f09426fdf8013966cd33875549503c39dbe4d20f85b077f713636aaa5f261843e
7
+ data.tar.gz: 4fbb5d4225d96a0be864450c6b3c2112fc544b4381e0c5d3e3fcf6c1ad08d0a2296ce93462023c2ffe4f4d8549f60ff566b64de74b28cc5049049561ea25d7c2
@@ -23,6 +23,7 @@ module Pod
23
23
  autoload :GitHub, 'cocoapods-core/github'
24
24
  autoload :HTTP, 'cocoapods-core/http'
25
25
  autoload :Lockfile, 'cocoapods-core/lockfile'
26
+ autoload :Metrics, 'cocoapods-core/metrics'
26
27
  autoload :Platform, 'cocoapods-core/platform'
27
28
  autoload :Podfile, 'cocoapods-core/podfile'
28
29
  autoload :Source, 'cocoapods-core/source'
@@ -52,7 +52,7 @@ module Pod
52
52
  # @example Initialization with an external source.
53
53
  #
54
54
  # Dependency.new('libPusher', {:git => 'example.com/repo.git'})
55
- # Dependency.new('libPusher', {:path => 'path/to/folder'})
55
+ # Dependency.new('libPusher', {:path => 'path/to/folder'})
56
56
  # Dependency.new('libPusher', {:podspec => 'example.com/libPusher.podspec'})
57
57
  #
58
58
  # @overload initialize(name, is_head)
@@ -324,12 +324,12 @@ module Pod
324
324
  # @return [Dependency] the dependency described by the string.
325
325
  #
326
326
  def self.from_string(string)
327
- match_data = string.match(/(\S*)( (.*))?/)
327
+ match_data = string.match(/((?:\s?[^\s(])+)( (?:.*))?/)
328
328
  name = match_data[1]
329
329
  version = match_data[2]
330
330
  version = version.gsub(/[()]/, '') if version
331
331
  case version
332
- when nil || /from `(.*)(`|')/
332
+ when nil, /from `(.*)(`|')/
333
333
  Dependency.new(name)
334
334
  when /HEAD/
335
335
  Dependency.new(name, :head)
@@ -1,5 +1,5 @@
1
1
  module Pod
2
2
  # The version of the cocoapods-core.
3
3
  #
4
- CORE_VERSION = '0.35.0' unless defined? Pod::CORE_VERSION
4
+ CORE_VERSION = '0.36.0.beta.1' unless defined? Pod::CORE_VERSION
5
5
  end
@@ -0,0 +1,47 @@
1
+ module Pod
2
+ # Allows to access metrics about pods.
3
+ #
4
+ # This class is stored in Core because it might be used by web services.
5
+ #
6
+ module Metrics
7
+ # Returns the metrics of a pod.
8
+ #
9
+ # @param [String] name
10
+ # The name of the pod.
11
+ #
12
+ # @return [Hash] The metrics for the pod.
13
+ #
14
+ def self.pod(name)
15
+ peform_request("http://metrics.cocoapods.org/api/v1/pods/#{name}")
16
+ end
17
+
18
+ private
19
+
20
+ # @!group Private helpers
21
+ #-------------------------------------------------------------------------#
22
+
23
+ # Performs a get request with the given URL.
24
+ #
25
+ # @param [String] url
26
+ # The URL of the resource.
27
+ #
28
+ # @return [Array, Hash] The information of the resource as Ruby objects.
29
+ #
30
+ def self.peform_request(url)
31
+ require 'rest'
32
+ require 'json'
33
+ headers = { 'User-Agent' => "CocoaPods #{Pod::CORE_VERSION}" }
34
+ response = REST.get(url, headers)
35
+ body = JSON.parse(response.body)
36
+ if response.ok?
37
+ body
38
+ else
39
+ CoreUI.warn "Request to #{url} failed - #{response.status_code}"
40
+ CoreUI.warn body['message']
41
+ nil
42
+ end
43
+ end
44
+
45
+ #-------------------------------------------------------------------------#
46
+ end
47
+ end
@@ -166,6 +166,16 @@ module Pod
166
166
  end
167
167
  end
168
168
 
169
+ # @return [Bool] whether the platform supports dynamic frameworks.
170
+ #
171
+ def supports_dynamic_frameworks?
172
+ if name == :ios
173
+ deployment_target && (deployment_target >= Version.new(8.0))
174
+ else
175
+ true
176
+ end
177
+ end
178
+
169
179
  # Converts the symbolic name of a platform to a string name suitable to be
170
180
  # presented to the user.
171
181
  #
@@ -101,6 +101,12 @@ module Pod
101
101
  get_hash_value('sources') || []
102
102
  end
103
103
 
104
+ # @return [Hash<String, Hash>] The plugins, keyed by name.
105
+ #
106
+ def plugins
107
+ get_hash_value('plugins') || {}
108
+ end
109
+
104
110
  # @return [String] the path of the workspace if specified by the user.
105
111
  #
106
112
  def workspace_path
@@ -182,6 +188,7 @@ module Pod
182
188
  sources
183
189
  generate_bridge_support
184
190
  set_arc_compatibility_flag
191
+ plugins
185
192
  ).freeze
186
193
 
187
194
  # @return [Hash] The hash representation of the Podfile.
@@ -415,6 +415,16 @@ module Pod
415
415
  current_target_definition.inhibit_all_warnings = true
416
416
  end
417
417
 
418
+ # Use frameworks instead of static libraries for Pods.
419
+ #
420
+ # ------
421
+ #
422
+ # This attribute is inherited by child target definitions.
423
+ #
424
+ def use_frameworks!(flag = true)
425
+ current_target_definition.use_frameworks!(flag)
426
+ end
427
+
418
428
  #-----------------------------------------------------------------------#
419
429
 
420
430
  # @!group Workspace
@@ -525,6 +535,34 @@ module Pod
525
535
 
526
536
  #-----------------------------------------------------------------------#
527
537
 
538
+ # Specifies the plugins that should be used during installation.
539
+ #
540
+ # -----
541
+ #
542
+ # Use this method to specify a plugin that should be used during
543
+ # installation, along with the options that should be passed to the plugin
544
+ # when it is invoked.
545
+ #
546
+ # @param [String] name
547
+ # The name of the plugin.
548
+ #
549
+ # @param [Hash] options
550
+ # The optional options that should be passed to the plugin when
551
+ # its hooks are invoked.
552
+ #
553
+ # @example Specifying to use the `slather` and `cocoapods-keys` plugins.
554
+ #
555
+ # plugin 'cocoapods-keys', keyring: 'Eidolon'
556
+ # plugin 'slather'
557
+ #
558
+ # @return [void]
559
+ #
560
+ def plugin(name, options = {})
561
+ hash_plugins = get_hash_value('plugins') || {}
562
+ (hash_plugins[name] ||= {}).merge!(options.deep_stringify_keys)
563
+ set_hash_value('plugins', hash_plugins)
564
+ end
565
+
528
566
  # This hook allows you to make any changes to the Pods after they have
529
567
  # been downloaded but before they are installed.
530
568
  #
@@ -316,6 +316,30 @@ module Pod
316
316
 
317
317
  #--------------------------------------#
318
318
 
319
+ # Sets whether the target definition should build a framework.
320
+ #
321
+ # @param [Bool] flag
322
+ # Whether a framework should be built.
323
+ #
324
+ # @return [void]
325
+ #
326
+ def use_frameworks!(flag = true)
327
+ set_hash_value('uses_frameworks', flag)
328
+ end
329
+
330
+ # @return [Bool] whether the target definition should build
331
+ # a framework.
332
+ #
333
+ def uses_frameworks?
334
+ if internal_hash['uses_frameworks'].nil?
335
+ root? ? false : parent.uses_frameworks?
336
+ else
337
+ get_hash_value('uses_frameworks')
338
+ end
339
+ end
340
+
341
+ #--------------------------------------#
342
+
319
343
  # Whether a specific pod should be linked to the target when building for
320
344
  # a specific configuration. If a pod has not been explicitly whitelisted
321
345
  # for any configuration, it is implicitly whitelisted.
@@ -507,6 +531,7 @@ module Pod
507
531
  dependencies
508
532
  children
509
533
  configuration_pod_whitelist
534
+ uses_frameworks
510
535
  ).freeze
511
536
 
512
537
  # @return [Hash] The hash representation of the target definition.
@@ -142,6 +142,34 @@ module Pod
142
142
  full_name.split('/').first
143
143
  end
144
144
 
145
+ # Returns the module name of a specification
146
+ #
147
+ # @return [String] the module name
148
+ #
149
+ def module_name
150
+ attributes_hash['module_name'] ||
151
+ c99ext_identifier(attributes_hash['header_dir']) ||
152
+ c99ext_identifier(attributes_hash['name'])
153
+ end
154
+
155
+ private
156
+
157
+ # Transforms the given string into a valid +identifier+ after C99ext
158
+ # standard, so that it can be used in source code where escaping of
159
+ # ambiguous characters is not applicable.
160
+ #
161
+ # @param [String] name
162
+ # any name, which may contain leading numbers, spaces or invalid
163
+ # characters.
164
+ #
165
+ # @return [String]
166
+ #
167
+ def c99ext_identifier(name)
168
+ return nil if name.nil?
169
+ I18n.transliterate(name).gsub(/^([0-9])/, '_\1').
170
+ gsub(/[^a-zA-Z0-9_]/, '_').gsub(/_+/, '_')
171
+ end
172
+
145
173
  #-------------------------------------------------------------------------#
146
174
 
147
175
  public
@@ -204,6 +232,9 @@ module Pod
204
232
  def subspec_by_name(relative_name, raise_if_missing = true)
205
233
  if relative_name.nil? || relative_name == base_name
206
234
  self
235
+ elsif relative_name.downcase == base_name.downcase
236
+ raise Informative, "Trying to access a `#{relative_name}` " \
237
+ "specification from `#{base_name}`, which has a different case."
207
238
  else
208
239
  remainder = relative_name[base_name.size + 1..-1]
209
240
  subspec_name = remainder.split('/').shift
@@ -216,7 +247,7 @@ module Pod
216
247
  return nil
217
248
  end
218
249
  end
219
- subspec.subspec_by_name(remainder)
250
+ subspec.subspec_by_name(remainder, raise_if_missing)
220
251
  end
221
252
  end
222
253
 
@@ -506,6 +537,7 @@ module Pod
506
537
  end
507
538
 
508
539
  # Loads a specification with the given string.
540
+ # The specification is evaluated in the context of `path`.
509
541
  #
510
542
  # @param [String] spec_contents
511
543
  # A string describing a specification.
@@ -517,16 +549,19 @@ module Pod
517
549
  #
518
550
  def self.from_string(spec_contents, path, subspec_name = nil)
519
551
  path = Pathname.new(path)
520
- case path.extname
521
- when '.podspec'
522
- spec = ::Pod._eval_podspec(spec_contents, path)
523
- unless spec.is_a?(Specification)
524
- raise Informative, "Invalid podspec file at path `#{path}`."
552
+ spec = nil
553
+ Dir.chdir(path.parent.directory? ? path.parent : Dir.pwd) do
554
+ case path.extname
555
+ when '.podspec'
556
+ spec = ::Pod._eval_podspec(spec_contents, path)
557
+ unless spec.is_a?(Specification)
558
+ raise Informative, "Invalid podspec file at path `#{path}`."
559
+ end
560
+ when '.json'
561
+ spec = Specification.from_json(spec_contents)
562
+ else
563
+ raise Informative, "Unsupported specification format `#{path.extname}`."
525
564
  end
526
- when '.json'
527
- spec = Specification.from_json(spec_contents)
528
- else
529
- raise Informative, "Unsupported specification format `#{path.extname}`."
530
565
  end
531
566
 
532
567
  spec.defined_in_file = path
@@ -96,6 +96,10 @@ module Pod
96
96
  #
97
97
  spec_attr_accessor :prefix_header_file
98
98
 
99
+ # @return [String] the module name.
100
+ #
101
+ spec_attr_accessor :module_name
102
+
99
103
  # @return [String] the headers directory.
100
104
  #
101
105
  spec_attr_accessor :header_dir
@@ -780,6 +780,25 @@ module Pod
780
780
 
781
781
  #------------------#
782
782
 
783
+ # @!method module_name=(name)
784
+ #
785
+ # The name to use for the framework / clang module which
786
+ # will be generated for this specification instead of the
787
+ # default (header_dir if set, otherwise the specification
788
+ # name).
789
+ #
790
+ # @example
791
+ #
792
+ # spec.module_name = 'Three20'
793
+ #
794
+ # @param [String] name
795
+ # the module name.
796
+ #
797
+ root_attribute :module_name,
798
+ :inherited => true
799
+
800
+ #------------------#
801
+
783
802
  # @!method header_dir=(dir)
784
803
  #
785
804
  # The directory where to store the headers files so they don't break
@@ -8,6 +8,13 @@ module Pod
8
8
  to_hash.to_json(*a) << "\n"
9
9
  end
10
10
 
11
+ # @return [String] the pretty json representation of the specification.
12
+ #
13
+ def to_pretty_json(*a)
14
+ require 'json'
15
+ JSON.pretty_generate(to_hash, *a) << "\n"
16
+ end
17
+
11
18
  #-----------------------------------------------------------------------#
12
19
 
13
20
  # @return [Hash] the hash representation of the specification including
@@ -50,8 +50,8 @@ module Pod
50
50
  run_root_validation_hooks
51
51
  perform_all_specs_analysis
52
52
  else
53
- results.add_error "[spec] The specification defined in `#{file}` "\
54
- "could not be loaded.\n\n#{@raise_message}"
53
+ results.add_error('spec', "The specification defined in `#{file}` "\
54
+ "could not be loaded.\n\n#{@raise_message}")
55
55
  end
56
56
  results.empty?
57
57
  end
@@ -91,10 +91,10 @@ module Pod
91
91
  next unless attr.required?
92
92
  unless value && (!value.respond_to?(:empty?) || !value.empty?)
93
93
  if attr.name == :license
94
- results.add_warning('[attributes] Missing required attribute' \
94
+ results.add_warning('attributes', 'Missing required attribute' \
95
95
  "`#{attr.name}`.")
96
96
  else
97
- results.add_error('[attributes] Missing required attribute' \
97
+ results.add_error('attributes', 'Missing required attribute' \
98
98
  "`#{attr.name}`.")
99
99
  end
100
100
  end
@@ -176,28 +176,36 @@ module Pod
176
176
  ]
177
177
  names_match = acceptable_names.include?(file.basename.to_s)
178
178
  unless names_match
179
- results.add_error '[name] The name of the spec should match the ' \
180
- 'name of the file.'
179
+ results.add_error('name', 'The name of the spec should match the ' \
180
+ 'name of the file.')
181
181
  end
182
182
 
183
183
  if spec.root.name =~ /\s/
184
- results.add_error '[name] The name of a spec should not contain ' \
185
- 'whitespace.'
184
+ results.add_error('name', 'The name of a spec should not contain ' \
185
+ 'whitespace.')
186
186
  end
187
187
 
188
188
  if spec.root.name[0, 1] == '.'
189
- results.add_error '[name] The name of a spec should not begin' \
190
- ' with a period.'
189
+ results.add_error('name', 'The name of a spec should not begin' \
190
+ ' with a period.')
191
191
  end
192
192
  end
193
193
  end
194
194
 
195
195
  def _validate_version(v)
196
196
  if v.to_s.empty?
197
- results.add_error '[version] A version is required.'
197
+ results.add_error('version', 'A version is required.')
198
198
  elsif v <= Version::ZERO
199
- results.add_error '[version] The version of the spec should be' \
200
- ' higher than 0.'
199
+ results.add_error('version', 'The version of the spec should be' \
200
+ ' higher than 0.')
201
+ end
202
+ end
203
+
204
+ # Performs validations related to the `module_name` attribute.
205
+ def _validate_module_name(m)
206
+ unless m.nil? || m =~ /^[a-z_][0-9a-z_]*$/i
207
+ results.add_error('module_name', 'The module name of a spec' \
208
+ ' should be a valid C99 identifier.')
201
209
  end
202
210
  end
203
211
 
@@ -205,11 +213,11 @@ module Pod
205
213
  #
206
214
  def _validate_summary(s)
207
215
  if s.length > 140
208
- results.add_warning '[summary] The summary should be a short ' \
209
- 'version of `description` (max 140 characters).'
216
+ results.add_warning('summary', 'The summary should be a short ' \
217
+ 'version of `description` (max 140 characters).')
210
218
  end
211
219
  if s =~ /A short description of/
212
- results.add_warning '[summary] The summary is not meaningful.'
220
+ results.add_warning('summary', 'The summary is not meaningful.')
213
221
  end
214
222
  end
215
223
 
@@ -217,15 +225,15 @@ module Pod
217
225
  #
218
226
  def _validate_description(d)
219
227
  if d =~ /An optional longer description of/
220
- results.add_warning '[description] The description is not meaningful.'
228
+ results.add_warning('description', 'The description is not meaningful.')
221
229
  end
222
230
  if d == spec.summary
223
- results.add_warning '[description] The description is equal to' \
224
- ' the summary.'
231
+ results.add_warning('description', 'The description is equal to' \
232
+ ' the summary.')
225
233
  end
226
234
  if d.length < spec.summary.length
227
- results.add_warning '[description] The description is shorter ' \
228
- 'than the summary.'
235
+ results.add_warning('description', 'The description is shorter ' \
236
+ 'than the summary.')
229
237
  end
230
238
  end
231
239
 
@@ -233,8 +241,8 @@ module Pod
233
241
  #
234
242
  def _validate_homepage(h)
235
243
  if h =~ %r{http://EXAMPLE}
236
- results.add_warning '[homepage] The homepage has not been updated' \
237
- ' from default'
244
+ results.add_warning('homepage', 'The homepage has not been updated' \
245
+ ' from default')
238
246
  end
239
247
  end
240
248
 
@@ -242,8 +250,8 @@ module Pod
242
250
  #
243
251
  def _validate_frameworks(frameworks)
244
252
  if frameworks_invalid?(frameworks)
245
- results.add_error '[frameworks] A framework should only be' \
246
- ' specified by its name'
253
+ results.add_error('frameworks', 'A framework should only be' \
254
+ ' specified by its name')
247
255
  end
248
256
  end
249
257
 
@@ -251,8 +259,8 @@ module Pod
251
259
  #
252
260
  def _validate_weak_frameworks(frameworks)
253
261
  if frameworks_invalid?(frameworks)
254
- results.add_error '[weak_frameworks] A weak framework should only be' \
255
- ' specified by its name'
262
+ results.add_error('weak_frameworks', 'A weak framework should only be' \
263
+ ' specified by its name')
256
264
  end
257
265
  end
258
266
 
@@ -262,20 +270,20 @@ module Pod
262
270
  libs.each do |lib|
263
271
  lib = lib.downcase
264
272
  if lib.end_with?('.a') || lib.end_with?('.dylib')
265
- results.add_error '[libraries] Libraries should not include the' \
273
+ results.add_error('libraries', 'Libraries should not include the' \
266
274
  ' extension ' \
267
- "(`#{lib}`)"
275
+ "(`#{lib}`)")
268
276
  end
269
277
 
270
278
  if lib.start_with?('lib')
271
- results.add_error '[libraries] Libraries should omit the `lib`' \
279
+ results.add_error('libraries', 'Libraries should omit the `lib`' \
272
280
  ' prefix ' \
273
- " (`#{lib}`)"
281
+ " (`#{lib}`)")
274
282
  end
275
283
 
276
284
  if lib.include?(',')
277
- results.add_error '[libraries] Libraries should not include comas ' \
278
- "(`#{lib}`)"
285
+ results.add_error('libraries', 'Libraries should not include comas ' \
286
+ "(`#{lib}`)")
279
287
  end
280
288
  end
281
289
  end
@@ -286,16 +294,16 @@ module Pod
286
294
  type = l[:type]
287
295
  file = l[:file]
288
296
  if type.nil?
289
- results.add_warning '[license] Missing license type.'
297
+ results.add_warning('license', 'Missing license type.')
290
298
  end
291
299
  if type && type.gsub(' ', '').gsub("\n", '').empty?
292
- results.add_warning '[license] Invalid license type.'
300
+ results.add_warning('license', 'Invalid license type.')
293
301
  end
294
302
  if type && type =~ /\(example\)/
295
- results.add_error '[license] Sample license type.'
303
+ results.add_error('license', 'Sample license type.')
296
304
  end
297
305
  if file && Pathname.new(file).extname !~ /^(\.(txt|md|markdown|))?$/i
298
- results.add_error '[license] Invalid file type'
306
+ results.add_error('license', 'Invalid file type')
299
307
  end
300
308
  end
301
309
 
@@ -307,19 +315,19 @@ module Pod
307
315
  version = spec.version.to_s
308
316
 
309
317
  if git =~ %r{http://EXAMPLE}
310
- results.add_error '[source] The Git source still contains the ' \
311
- 'example URL.'
318
+ results.add_error('source', 'The Git source still contains the ' \
319
+ 'example URL.')
312
320
  end
313
321
  if commit && commit.downcase =~ /head/
314
- results.add_error '[source] The commit of a Git source cannot be' \
315
- ' `HEAD`.'
322
+ results.add_error('source', 'The commit of a Git source cannot be' \
323
+ ' `HEAD`.')
316
324
  end
317
325
  if tag && !tag.to_s.include?(version)
318
- results.add_warning '[source] The version should be included in' \
319
- ' the Git tag.'
326
+ results.add_warning('source', 'The version should be included in' \
327
+ ' the Git tag.')
320
328
  end
321
329
  if tag.nil?
322
- results.add_warning '[source] Git sources should specify a tag.'
330
+ results.add_warning('source', 'Git sources should specify a tag.')
323
331
  end
324
332
  end
325
333
 
@@ -327,6 +335,15 @@ module Pod
327
335
  check_git_ssh_source(s)
328
336
  end
329
337
 
338
+ # Performs validations related to the `deprecated_in_favor_of` attribute.
339
+ #
340
+ def _validate_deprecated_in_favor_of(d)
341
+ if d == Specification.root_name(d)
342
+ results.add_error('deprecated_in_favor_of', 'a spec cannot be ' \
343
+ 'deprecated in favor of itself')
344
+ end
345
+ end
346
+
330
347
  # Performs validations related to github sources.
331
348
  #
332
349
  def perform_github_source_checks(s)
@@ -335,20 +352,22 @@ module Pod
335
352
  if git = s[:git]
336
353
  return unless git =~ /^#{URI.regexp}$/
337
354
  git_uri = URI.parse(git)
338
- if git_uri.host == 'www.github.com'
339
- results.add_warning '[github_sources] Github repositories should ' \
340
- 'not use `www` in their URL.'
341
- end
342
- if git_uri.host == 'github.com' || git_uri.host == 'gist.github.com'
343
- unless git.end_with?('.git')
344
- results.add_warning '[github_sources] Github repositories ' \
345
- 'should end in `.git`.'
346
- end
347
- unless git_uri.scheme == 'https'
348
- results.add_warning '[github_sources] Github repositories ' \
349
- 'should use an `https` link.'
350
- end
351
- end
355
+ perform_github_uri_checks(git, git_uri) if git_uri.host.end_with?('github.com')
356
+ end
357
+ end
358
+
359
+ def perform_github_uri_checks(git, git_uri)
360
+ if git_uri.host.start_with?('www.')
361
+ results.add_warning('github_sources', 'Github repositories should ' \
362
+ 'not use `www` in their URL.')
363
+ end
364
+ unless git.end_with?('.git')
365
+ results.add_warning('github_sources', 'Github repositories ' \
366
+ 'should end in `.git`.')
367
+ end
368
+ unless git_uri.scheme == 'https'
369
+ results.add_warning('github_sources', 'Github repositories ' \
370
+ 'should use an `https` link.')
352
371
  end
353
372
  end
354
373
 
@@ -357,9 +376,9 @@ module Pod
357
376
  def check_git_ssh_source(s)
358
377
  if git = s[:git]
359
378
  if git =~ /\w+\@(\w|\.)+\:(\/\w+)*/
360
- results.add_warning '[source] Git SSH URLs will NOT work for ' \
379
+ results.add_warning('source', 'Git SSH URLs will NOT work for ' \
361
380
  'people behind firewalls configured to only allow HTTP, ' \
362
- 'therefore HTTPS is preferred.'
381
+ 'therefore HTTPS is preferred.')
363
382
  end
364
383
  end
365
384
  end
@@ -368,8 +387,8 @@ module Pod
368
387
  #
369
388
  def _validate_social_media_url(s)
370
389
  if s =~ %r{https://twitter.com/EXAMPLE}
371
- results.add_warning '[social_media_url] The social media URL has ' \
372
- 'not been updated from the default.'
390
+ results.add_warning('social_media_url', 'The social media URL has ' \
391
+ 'not been updated from the default.')
373
392
  end
374
393
  end
375
394
 
@@ -383,8 +402,8 @@ module Pod
383
402
  #
384
403
  def _validate_compiler_flags(flags)
385
404
  if flags.join(' ').split(' ').any? { |flag| flag.start_with?('-Wno') }
386
- results.add_warning '[compiler_flags] Warnings must not be disabled' \
387
- '(`-Wno compiler` flags).'
405
+ results.add_warning('compiler_flags', 'Warnings must not be disabled' \
406
+ '(`-Wno compiler` flags).')
388
407
  end
389
408
  end
390
409
 
@@ -53,7 +53,7 @@ module Pod
53
53
  unknown_keys = keys - valid_keys
54
54
 
55
55
  unknown_keys.each do |key|
56
- results.add_warning "Unrecognized `#{key}` key"
56
+ results.add_warning('attributes', "Unrecognized `#{key}` key")
57
57
  end
58
58
 
59
59
  Pod::Specification::DSL.attributes.each do |_key, attribute|
@@ -84,8 +84,8 @@ module Pod
84
84
  if patterns.respond_to?(:each)
85
85
  patterns.each do |pattern|
86
86
  if pattern.start_with?('/')
87
- results.add_error '[File Patterns] File patterns must be ' \
88
- "relative and cannot start with a slash (#{attrb.name})."
87
+ results.add_error('File Patterns', 'File patterns must be ' \
88
+ "relative and cannot start with a slash (#{attrb.name}).")
89
89
  end
90
90
  end
91
91
  end
@@ -100,10 +100,10 @@ module Pod
100
100
  empty_patterns = methods.all? { |m| consumer.send(m).empty? }
101
101
  empty = empty_patterns && consumer.spec.subspecs.empty?
102
102
  if empty
103
- results.add_error "[File Patterns] The #{consumer.spec} spec is " \
103
+ results.add_error('File Patterns', "The #{consumer.spec} spec is " \
104
104
  'empty (no source files, resources, resource_bundles, ' \
105
105
  'preserve paths, vendored_libraries, vendored_frameworks, ' \
106
- 'dependencies, nor subspecs).'
106
+ 'dependencies, nor subspecs).')
107
107
  end
108
108
  end
109
109
 
@@ -129,29 +129,29 @@ module Pod
129
129
  def validate_attribute_array_keys(attribute, value)
130
130
  unknown_keys = value.keys.map(&:to_s) - attribute.keys.map(&:to_s)
131
131
  unknown_keys.each do |unknown_key|
132
- results.add_warning "Unrecognized `#{unknown_key}` key for " \
133
- "`#{attribute.name}` attribute."
132
+ results.add_warning('keys', "Unrecognized `#{unknown_key}` key for " \
133
+ "`#{attribute.name}` attribute.")
134
134
  end
135
135
  end
136
136
 
137
137
  def validate_attribute_hash_keys(attribute, value)
138
138
  major_keys = value.keys & attribute.keys.keys
139
139
  if major_keys.count.zero?
140
- results.add_warning "Missing primary key for `#{attribute.name}` " \
140
+ results.add_warning('keys', "Missing primary key for `#{attribute.name}` " \
141
141
  'attribute. The acceptable ones are: ' \
142
- "`#{attribute.keys.keys.map(&:to_s).sort.join(', ')}`."
142
+ "`#{attribute.keys.keys.map(&:to_s).sort.join(', ')}`.")
143
143
  elsif major_keys.count == 1
144
144
  acceptable = attribute.keys[major_keys.first] || []
145
145
  unknown = value.keys - major_keys - acceptable
146
146
  unless unknown.empty?
147
- results.add_warning "Incompatible `#{unknown.sort.join(', ')}` " \
147
+ results.add_warning('keys', "Incompatible `#{unknown.sort.join(', ')}` " \
148
148
  "key(s) with `#{major_keys.first}` primary key for " \
149
- "`#{attribute.name}` attribute."
149
+ "`#{attribute.name}` attribute.")
150
150
  end
151
151
  else
152
152
  sorted_keys = major_keys.map(&:to_s).sort
153
- results.add_warning "Incompatible `#{sorted_keys.join(', ')}` " \
154
- "keys for `#{attribute.name}` attribute."
153
+ results.add_warning('keys', "Incompatible `#{sorted_keys.join(', ')}` " \
154
+ "keys for `#{attribute.name}` attribute.")
155
155
  end
156
156
  end
157
157
  end
@@ -9,6 +9,10 @@ module Pod
9
9
  #
10
10
  attr_reader :type
11
11
 
12
+ # @return[String] the name of the attribute associated with result.
13
+ #
14
+ attr_reader :attribute_name
15
+
12
16
  # @return [String] the message associated with result.
13
17
  #
14
18
  attr_reader :message
@@ -16,8 +20,9 @@ module Pod
16
20
  # @param [Symbol] type @see type
17
21
  # @param [String] message @see message
18
22
  #
19
- def initialize(type, message)
23
+ def initialize(type, attribute_name, message)
20
24
  @type = type
25
+ @attribute_name = attribute_name
21
26
  @message = message
22
27
  @platforms = []
23
28
  end
@@ -30,7 +35,7 @@ module Pod
30
35
  # @return [String] a string representation suitable for UI output.
31
36
  #
32
37
  def to_s
33
- r = "[#{type.to_s.upcase}] #{message}"
38
+ r = "[#{type.to_s.upcase}] [#{attribute_name}] #{message}"
34
39
  if platforms != Specification::PLATFORMS
35
40
  platforms_names = platforms.uniq.map do |p|
36
41
  Platform.string_name(p)
@@ -67,8 +72,8 @@ module Pod
67
72
  #
68
73
  # @return [void]
69
74
  #
70
- def add_error(message)
71
- add_result(:error, message)
75
+ def add_error(attribute_name, message)
76
+ add_result(:error, attribute_name, message)
72
77
  end
73
78
 
74
79
  # Adds a warning result with the given message.
@@ -78,8 +83,8 @@ module Pod
78
83
  #
79
84
  # @return [void]
80
85
  #
81
- def add_warning(message)
82
- add_result(:warning, message)
86
+ def add_warning(attribute_name, message)
87
+ add_result(:warning, attribute_name, message)
83
88
  end
84
89
 
85
90
  private
@@ -101,10 +106,12 @@ module Pod
101
106
  #
102
107
  # @return [void]
103
108
  #
104
- def add_result(type, message)
105
- result = results.find { |r| r.type == type && r.message == message }
109
+ def add_result(type, attribute_name, message)
110
+ result = results.find do |r|
111
+ r.type == type && r.attribute_name == attribute_name && r.message == message
112
+ end
106
113
  unless result
107
- result = Result.new(type, message)
114
+ result = Result.new(type, attribute_name, message)
108
115
  results << result
109
116
  end
110
117
  result.platforms << @consumer.platform_name if @consumer
@@ -1,6 +1,5 @@
1
1
  require 'active_support/core_ext/array/conversions'
2
2
  require 'cocoapods-core/specification/set/presenter'
3
- require 'cocoapods-core/specification/set/statistics'
4
3
 
5
4
  module Pod
6
5
  class Specification
@@ -11,15 +11,10 @@ module Pod
11
11
  #
12
12
  attr_reader :set
13
13
 
14
- # @return [Statistics] The statistics provider.
15
- #
16
- attr_reader :statistics_provider
17
-
18
14
  # @param [Set] set @see #set.
19
15
  #
20
- def initialize(set, statistics_provider = nil)
16
+ def initialize(set)
21
17
  @set = set
22
- @statistics_provider = statistics_provider || Statistics.instance
23
18
  end
24
19
 
25
20
  #---------------------------------------------------------------------#
@@ -54,7 +49,7 @@ module Pod
54
49
  #
55
50
  # @note This method orders the sources by name.
56
51
  #
57
- def verions_by_source
52
+ def versions_by_source
58
53
  result = []
59
54
  versions_by_source = @set.versions_by_source
60
55
  @set.sources.sort.each do |source|
@@ -179,71 +174,29 @@ module Pod
179
174
 
180
175
  # @!group Statistics
181
176
 
182
- # @return [Time] the creation date of the first known `podspec` of the
183
- # Pod.
184
- #
185
- def creation_date
186
- statistics_provider.creation_date(@set)
187
- end
188
-
189
177
  # @return [Integer] the GitHub likes of the repo of the Pod.
190
178
  #
191
- def github_watchers
192
- statistics_provider.github_watchers(@set)
179
+ def github_stargazers
180
+ github_metrics['stargazers']
193
181
  end
194
182
 
195
183
  # @return [Integer] the GitHub forks of the repo of the Pod.
196
184
  #
197
185
  def github_forks
198
- statistics_provider.github_forks(@set)
199
- end
200
-
201
- # @return [String] the relative time of the last push of the repo the Pod.
202
- #
203
- def github_last_activity
204
- distance_from_now_in_words(statistics_provider.github_pushed_at(@set))
186
+ github_metrics['forks']
205
187
  end
206
188
 
207
189
  #---------------------------------------------------------------------#
208
190
 
209
- private
191
+ # @!group Private Helpers
210
192
 
211
- # Computes a human readable string that represents a past date in
212
- # relative terms.
213
- #
214
- # @param [Time, String] from_time
215
- # the date that should be represented.
216
- #
217
- # @example Possible outputs
218
- #
219
- # "less than a week ago"
220
- # "15 days ago"
221
- # "3 month ago"
222
- # "more than a year ago"
223
- #
224
- # @return [String] a string that represents a past date.
225
- #
226
- def distance_from_now_in_words(from_time)
227
- return nil unless from_time
228
- from_time = Time.parse(from_time) unless from_time.is_a?(Time)
229
- to_time = Time.now
230
- distance_in_days = (((to_time - from_time).abs) / 60 / 60 / 24).round
231
-
232
- case distance_in_days
233
- when 0..7
234
- 'less than a week ago'
235
- when 8..29
236
- "#{distance_in_days} days ago"
237
- when 30..45
238
- '1 month ago'
239
- when 46..365
240
- "#{(distance_in_days.to_f / 30).round} months ago"
241
- else
242
- 'more than a year ago'
243
- end
193
+ def metrics
194
+ @metrics ||= Metrics.pod(name) || {}
244
195
  end
245
196
 
246
- #---------------------------------------------------------------------#
197
+ def github_metrics
198
+ metrics['github'] || {}
199
+ end
247
200
  end
248
201
  end
249
202
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.35.0
4
+ version: 0.36.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Duran
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-11-19 00:00:00.000000000 Z
12
+ date: 2014-12-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -84,6 +84,7 @@ files:
84
84
  - lib/cocoapods-core/github.rb
85
85
  - lib/cocoapods-core/http.rb
86
86
  - lib/cocoapods-core/lockfile.rb
87
+ - lib/cocoapods-core/metrics.rb
87
88
  - lib/cocoapods-core/platform.rb
88
89
  - lib/cocoapods-core/podfile/dsl.rb
89
90
  - lib/cocoapods-core/podfile/target_definition.rb
@@ -105,7 +106,6 @@ files:
105
106
  - lib/cocoapods-core/specification/linter.rb
106
107
  - lib/cocoapods-core/specification/root_attribute_accessors.rb
107
108
  - lib/cocoapods-core/specification/set/presenter.rb
108
- - lib/cocoapods-core/specification/set/statistics.rb
109
109
  - lib/cocoapods-core/specification/set.rb
110
110
  - lib/cocoapods-core/specification.rb
111
111
  - lib/cocoapods-core/standard_error.rb
@@ -1,263 +0,0 @@
1
- module Pod
2
- class Specification
3
- class Set
4
- # The statistics class provides information about one or more {Set} that
5
- # is not readily available because expensive to compute or provided by a
6
- # remote source.
7
- #
8
- # The class provides also facilities to work with a collection of sets.
9
- # It always caches in memory the computed values and it can take an
10
- # optional path to cache file that it is responsible of populating and
11
- # invalidating.
12
- #
13
- # To reuse the in memory cache and to minimize the disk access to the
14
- # cache file a shared instance is also available.
15
- #
16
- class Statistics
17
- # @return [Statistics] the shared statistics instance.
18
- #
19
- def self.instance
20
- @instance ||= new
21
- end
22
-
23
- # Allows to set the shared instance.
24
- #
25
- # @param [Statistics] instance
26
- # the new shared instance or nil.
27
- #
28
- # @return [Statistics] the shared statistics instance.
29
- #
30
- class << self
31
- attr_writer :instance
32
- end
33
-
34
- # @return [Pathname] the path to the optional cache file.
35
- #
36
- # @note The cache file can be specified after initialization, but
37
- # it has to be configured before requiring any value, otherwise
38
- # it is ignored.
39
- #
40
- attr_accessor :cache_file
41
-
42
- # @return [Integer] the number of seconds after which the caches of
43
- # values that might changed are discarded.
44
- #
45
- # @note If not specified on initialization defaults to 3 days.
46
- #
47
- attr_accessor :cache_expiration
48
-
49
- # @param [Pathname] cache_file @see cache_file
50
- #
51
- # @param [Integer] cache_expiration @see cache_expiration
52
- #
53
- def initialize(cache_file = nil, cache_expiration = (60 * 60 * 24 * 3))
54
- require 'yaml'
55
-
56
- @cache_file = cache_file
57
- @cache_expiration = cache_expiration
58
- end
59
-
60
- #---------------------------------------------------------------------#
61
-
62
- # @!group Accessing the statistics
63
-
64
- # Computes the date in which the first podspec of a set was committed
65
- # on its git source.
66
- #
67
- # @param [Set] set
68
- # the set for the Pod whose creation date is needed.
69
- #
70
- # @note The set should be generated with only the source that is
71
- # analyzed. If there are more than one the first one is
72
- # processed.
73
- #
74
- # @note This method needs to traverse the git history of the repo and
75
- # thus incurs in a performance hit.
76
- #
77
- # @return [Time] the date in which a Pod appeared for the first time on
78
- # the {Source}.
79
- #
80
- def creation_date(set)
81
- date = compute_creation_date(set)
82
- save_cache
83
- date
84
- end
85
-
86
- # Computes the date in which the first podspec of each given set was
87
- # committed on its git source.
88
- #
89
- # @param [Array<Set>] sets
90
- # the list of the sets for the Pods whose creation date is
91
- # needed.
92
- #
93
- # @note @see creation_date
94
- #
95
- # @note This method is optimized for multiple sets because it saves
96
- # the cache file only once.
97
- #
98
- # @return [Array<Time>] the list of the dates in which the Pods
99
- # appeared for the first time on the {Source}.
100
- #
101
- def creation_dates(sets)
102
- dates = {}
103
- sets.each { |set| dates[set.name] = compute_creation_date(set) }
104
- save_cache
105
- dates
106
- end
107
-
108
- # Computes the number of likes that a Pod has on Github.
109
- #
110
- # @param [Set] set
111
- # the set of the Pod.
112
- #
113
- # @return [Integer] the number of likes or nil if the Pod is not hosted
114
- # on GitHub.
115
- #
116
- def github_watchers(set)
117
- github_stats_if_needed(set)
118
- get_value(set, :gh_watchers)
119
- end
120
-
121
- # Computes the number of forks that a Pod has on Github.
122
- #
123
- # @param [Set] set @see github_watchers
124
- #
125
- # @return [Integer] the number of forks or nil if the Pod is not hosted
126
- # on GitHub.
127
- #
128
- def github_forks(set)
129
- github_stats_if_needed(set)
130
- get_value(set, :gh_forks)
131
- end
132
-
133
- # Computes the number of likes that a Pod has on Github.
134
- #
135
- # @param [Set] set @see github_watchers
136
- #
137
- # @return [Time] the time of the last push or nil if the Pod is not
138
- # hosted on GitHub.
139
- #
140
- def github_pushed_at(set)
141
- github_stats_if_needed(set)
142
- string_time = get_value(set, :pushed_at)
143
- Time.parse(string_time) if string_time
144
- end
145
-
146
- #---------------------------------------------------------------------#
147
-
148
- private
149
-
150
- # @return [Hash{String => Hash}] the in-memory cache, where for each
151
- # set is stored a hash with the result of the computations.
152
- #
153
- def cache
154
- unless @cache
155
- if cache_file && cache_file.exist?
156
- @cache = YAMLHelper.load_string(cache_file.read)
157
- else
158
- @cache = {}
159
- end
160
- end
161
- @cache
162
- end
163
-
164
- # Returns the value for the given key of a set stored in the cache, if
165
- # available.
166
- #
167
- # @param [Set] set
168
- # the set for which the value is needed.
169
- #
170
- # @param [Symbol] key
171
- # the key of the value.
172
- #
173
- # @return [Object] the value or nil.
174
- #
175
- def get_value(set, key)
176
- if cache[set.name] && cache[set.name][key]
177
- cache[set.name][key]
178
- end
179
- end
180
-
181
- # Stores the given value of a set for the given key in the cache.
182
- #
183
- # @param [Set] set
184
- # the set for which the value has to be stored.
185
- #
186
- # @param [Symbol] key
187
- # the key of the value.
188
- #
189
- # @param [Object] value
190
- # the value to store.
191
- #
192
- # @return [Object] the value or nil.
193
- #
194
- def set_value(set, key, value)
195
- cache[set.name] ||= {}
196
- cache[set.name][key] = value
197
- end
198
-
199
- # Saves the in-memory cache to the path of cache file if specified.
200
- #
201
- # @return [void]
202
- #
203
- def save_cache
204
- if cache_file
205
- yaml = YAML.dump(cache)
206
- File.open(cache_file, 'w') { |f| f.write(yaml) }
207
- end
208
- end
209
-
210
- # Analyzes the history of the git repository of the {Source} of the
211
- # given {Set} to find when its folder was created.
212
- #
213
- # @param [Set] set
214
- # the set for which the creation date is needed.
215
- #
216
- # @return [Time] the date in which a Pod was created.
217
- #
218
- def compute_creation_date(set)
219
- date = get_value(set, :creation_date)
220
- unless date
221
- Dir.chdir(set.sources.first.repo) do
222
- git_log = `git log --first-parent --format=%ct "#{set.name}"`
223
- creation_date = git_log.split("\n").last.to_i
224
- date = Time.at(creation_date)
225
- end
226
- set_value(set, :creation_date, date)
227
- end
228
- date
229
- end
230
-
231
- # Retrieved the GitHub information from the API for the given set and
232
- # stores it in the in-memory cache.
233
- #
234
- # @note If there is a valid cache and it was generated withing the
235
- # expiration time frame this method does nothing.
236
- #
237
- # @param [Set] set
238
- # the set for which the GitHub information is needed.
239
- #
240
- # @return [void]
241
- #
242
- def github_stats_if_needed(set)
243
- update_date = get_value(set, :gh_date)
244
- return if update_date && update_date > (Time.now - cache_expiration)
245
-
246
- spec = set.specification
247
- url = spec.source[:git]
248
- repo = GitHub.repo(url) if url
249
-
250
- if repo
251
- set_value(set, :gh_watchers, repo['watchers'])
252
- set_value(set, :gh_forks, repo['forks'])
253
- set_value(set, :pushed_at, repo['pushed_at'])
254
- set_value(set, :gh_date, Time.now)
255
- save_cache
256
- end
257
- end
258
-
259
- #---------------------------------------------------------------------#
260
- end
261
- end
262
- end
263
- end