cocoapods-core 0.35.0 → 0.36.0.beta.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.
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