cocoapods 1.4.0 → 1.5.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -0
  3. data/lib/cocoapods.rb +1 -0
  4. data/lib/cocoapods/command/outdated.rb +2 -2
  5. data/lib/cocoapods/command/repo/push.rb +5 -0
  6. data/lib/cocoapods/command/update.rb +21 -5
  7. data/lib/cocoapods/gem_version.rb +1 -1
  8. data/lib/cocoapods/generator/acknowledgements.rb +6 -3
  9. data/lib/cocoapods/generator/constant.rb +19 -0
  10. data/lib/cocoapods/generator/copy_resources_script.rb +15 -3
  11. data/lib/cocoapods/generator/embed_frameworks_script.rb +11 -2
  12. data/lib/cocoapods/generator/module_map.rb +56 -5
  13. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +19 -13
  14. data/lib/cocoapods/generator/xcconfig/pod_xcconfig.rb +4 -6
  15. data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +25 -2
  16. data/lib/cocoapods/installer.rb +17 -8
  17. data/lib/cocoapods/installer/analyzer.rb +48 -38
  18. data/lib/cocoapods/installer/analyzer/analysis_result.rb +11 -0
  19. data/lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb +7 -6
  20. data/lib/cocoapods/installer/analyzer/pod_variant.rb +8 -8
  21. data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +3 -3
  22. data/lib/cocoapods/installer/analyzer/podfile_dependency_cache.rb +54 -0
  23. data/lib/cocoapods/installer/analyzer/specs_state.rb +16 -16
  24. data/lib/cocoapods/installer/analyzer/target_inspector.rb +7 -11
  25. data/lib/cocoapods/installer/pod_source_installer.rb +2 -3
  26. data/lib/cocoapods/installer/podfile_validator.rb +11 -10
  27. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +72 -28
  28. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +8 -2
  29. data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +9 -0
  30. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +32 -24
  31. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +97 -54
  32. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_integrator.rb +9 -11
  33. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +4 -9
  34. data/lib/cocoapods/installer/xcode/target_validator.rb +32 -18
  35. data/lib/cocoapods/project.rb +32 -17
  36. data/lib/cocoapods/resolver.rb +59 -31
  37. data/lib/cocoapods/resolver/lazy_specification.rb +28 -18
  38. data/lib/cocoapods/sandbox.rb +2 -4
  39. data/lib/cocoapods/sandbox/file_accessor.rb +25 -9
  40. data/lib/cocoapods/sandbox/headers_store.rb +31 -6
  41. data/lib/cocoapods/sandbox/path_list.rb +36 -46
  42. data/lib/cocoapods/target.rb +7 -4
  43. data/lib/cocoapods/target/aggregate_target.rb +10 -8
  44. data/lib/cocoapods/target/pod_target.rb +87 -20
  45. data/lib/cocoapods/user_interface/error_report.rb +1 -1
  46. data/lib/cocoapods/user_interface/inspector_reporter.rb +4 -4
  47. data/lib/cocoapods/validator.rb +40 -29
  48. metadata +11 -9
@@ -81,11 +81,8 @@ module Pod
81
81
  end
82
82
 
83
83
  if target.requires_frameworks?
84
- framework_name = target.product_module_name
85
- settings['PRODUCT_NAME'] = framework_name
86
84
  if target.static_framework?
87
- settings['PUBLIC_HEADERS_FOLDER_PATH'] = framework_name + '.framework' + '/Headers'
88
- settings['PRIVATE_HEADERS_FOLDER_PATH'] = framework_name + '.framework' + '/PrivateHeaders'
85
+ settings['MACH_O_TYPE'] = 'staticlib'
89
86
  end
90
87
  else
91
88
  settings.merge!('OTHER_LDFLAGS' => '', 'OTHER_LIBTOOLFLAGS' => '')
@@ -105,6 +102,7 @@ module Pod
105
102
  # @return [Void]
106
103
  #
107
104
  def update_changed_file(generator, path)
105
+ path.dirname.mkpath
108
106
  if path.exist?
109
107
  generator.save_as(support_files_temp_dir)
110
108
  unless FileUtils.identical?(support_files_temp_dir, path)
@@ -169,9 +167,6 @@ module Pod
169
167
  # Creates the module map file which ensures that the umbrella header is
170
168
  # recognized with a customized path
171
169
  #
172
- # @yield_param [Generator::ModuleMap]
173
- # yielded once to configure the private headers
174
- #
175
170
  # @return [void]
176
171
  #
177
172
  def create_module_map
@@ -207,10 +202,10 @@ module Pod
207
202
  file_ref = add_file_to_support_group(path)
208
203
  native_target.add_file_references([file_ref])
209
204
 
210
- # Make the umbrella header public
205
+ acl = target.requires_frameworks? ? 'Public' : 'Project'
211
206
  build_file = native_target.headers_build_phase.build_file(file_ref)
212
207
  build_file.settings ||= {}
213
- build_file.settings['ATTRIBUTES'] = ['Public']
208
+ build_file.settings['ATTRIBUTES'] = [acl]
214
209
  end
215
210
  end
216
211
 
@@ -35,7 +35,7 @@ module Pod
35
35
  verify_no_duplicate_framework_and_library_names
36
36
  verify_no_static_framework_transitive_dependencies
37
37
  verify_no_pods_used_with_multiple_swift_versions
38
- verify_framework_usage
38
+ verify_swift_pods_have_module_dependencies
39
39
  end
40
40
 
41
41
  private
@@ -71,16 +71,22 @@ module Pod
71
71
  next unless aggregate_target.requires_frameworks?
72
72
 
73
73
  aggregate_target.user_build_configurations.keys.each do |config|
74
- pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
74
+ dynamic_pod_targets = aggregate_target.pod_targets_for_build_configuration(config).reject(&:static_framework?)
75
75
 
76
- dependencies = pod_targets.select(&:should_build?).reject(&:static_framework?).flat_map(&:dependencies)
77
- depended_upon_targets = pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
76
+ dependencies = dynamic_pod_targets.select(&:should_build?).flat_map(&:dependencies)
77
+ depended_upon_targets = dynamic_pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
78
78
 
79
79
  static_libs = depended_upon_targets.flat_map(&:file_accessors).flat_map(&:vendored_static_artifacts)
80
80
  unless static_libs.empty?
81
81
  raise Informative, "The '#{aggregate_target.label}' target has " \
82
82
  "transitive dependencies that include static binaries: (#{static_libs.to_sentence})"
83
83
  end
84
+
85
+ static_framework_deps = dynamic_pod_targets.select(&:should_build?).flat_map(&:recursive_dependent_targets).select(&:static_framework?)
86
+ unless static_framework_deps.empty?
87
+ raise Informative, "The '#{aggregate_target.label}' target has " \
88
+ "transitive dependencies that include static frameworks: (#{static_framework_deps.flat_map(&:name).to_sentence})"
89
+ end
84
90
  end
85
91
  end
86
92
  end
@@ -100,26 +106,34 @@ module Pod
100
106
 
101
107
  unless error_messages.empty?
102
108
  raise Informative, 'The following pods are integrated into targets ' \
103
- "that do not have the same Swift version:\n\n#{error_messages.join("\n")}"
109
+ "that do not have the same Swift version:\n\n#{error_messages.join("\n")}"
104
110
  end
105
111
  end
106
112
 
107
- def verify_framework_usage
108
- aggregate_targets.each do |aggregate_target|
109
- next if aggregate_target.requires_frameworks?
110
-
111
- aggregate_target.user_build_configurations.keys.each do |config|
112
- pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
113
+ def verify_swift_pods_have_module_dependencies
114
+ error_messages = []
115
+ pod_targets.each do |pod_target|
116
+ next unless pod_target.uses_swift?
113
117
 
114
- swift_pods = pod_targets.select(&:uses_swift?)
115
- unless swift_pods.empty?
116
- raise Informative, 'Pods written in Swift can only be integrated as frameworks; ' \
117
- 'add `use_frameworks!` to your Podfile or target to opt into using it. ' \
118
- "The Swift #{swift_pods.size == 1 ? 'Pod being used is' : 'Pods being used are'}: " +
119
- swift_pods.map(&:name).to_sentence
120
- end
118
+ non_module_dependencies = []
119
+ pod_target.dependent_targets.each do |dependent_target|
120
+ next if !dependent_target.should_build? || dependent_target.defines_module?
121
+ non_module_dependencies << dependent_target.name
121
122
  end
123
+
124
+ next if non_module_dependencies.empty?
125
+
126
+ error_messages << "The Swift pod `#{pod_target.name}` depends upon #{non_module_dependencies.map { |d| "`#{d}`" }.to_sentence}, " \
127
+ 'which do not define modules. ' \
128
+ 'To opt into those targets generating module maps '\
129
+ '(which is necessary to import them from Swift when building as static libraries), ' \
130
+ 'you may set `use_modular_headers!` globally in your Podfile, '\
131
+ 'or specify `:modular_headers => true` for particular dependencies.'
122
132
  end
133
+ return if error_messages.empty?
134
+
135
+ raise Informative, 'The following Swift pods cannot yet be integrated '\
136
+ "as static libraries:\n\n#{error_messages.join("\n\n")}"
123
137
  end
124
138
  end
125
139
  end
@@ -40,6 +40,23 @@ module Pod
40
40
  #
41
41
  attr_reader :development_pods
42
42
 
43
+ # Generates a list of new UUIDs that created objects can be assigned.
44
+ #
45
+ # @note Overridden to generate UUIDs in a much faster way, since we don't need to check for collisions
46
+ # (as the Pods project is regenerated each time, and thus all UUIDs will have come from this method)
47
+ #
48
+ # @param [Integer] count
49
+ # The number of UUIDs to generate
50
+ #
51
+ # @return [Void]
52
+ #
53
+ def generate_available_uuid_list(count = 100)
54
+ start = @generated_uuids.size
55
+ uniques = Array.new(count) { |i| format('%011X0', start + i) }
56
+ @generated_uuids += uniques
57
+ @available_uuids += uniques
58
+ end
59
+
43
60
  public
44
61
 
45
62
  # @!group Legacy Xcode build root
@@ -185,15 +202,14 @@ module Pod
185
202
  # @return [PBXFileReference] The new file reference.
186
203
  #
187
204
  def add_file_reference(absolute_path, group, reflect_file_system_structure = false, base_path = nil)
188
- file_path_name = absolute_path.is_a?(Pathname) ? absolute_path : Pathname.new(absolute_path)
189
- group = group_for_path_in_group(file_path_name, group, reflect_file_system_structure, base_path)
190
- if ref = reference_for_path(file_path_name.realpath)
191
- @refs_by_absolute_path[absolute_path.to_s] = ref
192
- ref
193
- else
194
- ref = group.new_file(file_path_name.realpath)
195
- @refs_by_absolute_path[absolute_path.to_s] = ref
205
+ file_path_name = absolute_path.is_a?(Pathname) ? absolute_path : Pathname(absolute_path)
206
+ if ref = reference_for_path(file_path_name)
207
+ return ref
196
208
  end
209
+
210
+ group = group_for_path_in_group(file_path_name, group, reflect_file_system_structure, base_path)
211
+ ref = group.new_file(file_path_name.realpath)
212
+ @refs_by_absolute_path[file_path_name.to_s] = ref
197
213
  end
198
214
 
199
215
  # Returns the file reference for the given absolute path.
@@ -205,11 +221,12 @@ module Pod
205
221
  # @return [Nil] If no file reference could be found.
206
222
  #
207
223
  def reference_for_path(absolute_path)
208
- unless Pathname.new(absolute_path).absolute?
224
+ absolute_path = absolute_path.is_a?(Pathname) ? absolute_path : Pathname(absolute_path)
225
+ unless absolute_path.absolute?
209
226
  raise ArgumentError, "Paths must be absolute #{absolute_path}"
210
227
  end
211
228
 
212
- refs_by_absolute_path[absolute_path.to_s]
229
+ refs_by_absolute_path[absolute_path.to_s] ||= refs_by_absolute_path[absolute_path.realpath.to_s]
213
230
  end
214
231
 
215
232
  # Adds a file reference to the Podfile.
@@ -336,28 +353,26 @@ module Pod
336
353
  relative_base = base_path.nil? ? group.real_path : base_path.realdirpath
337
354
  relative_pathname = absolute_pathname.relative_path_from(relative_base)
338
355
  relative_dir = relative_pathname.dirname
339
- lproj_regex = /\.lproj/i
340
356
 
341
357
  # Add subgroups for directories, but treat .lproj as a file
342
358
  if reflect_file_system_structure
343
359
  path = relative_base
344
360
  relative_dir.each_filename do |name|
345
- break if name.to_s =~ lproj_regex
361
+ break if name.to_s.downcase.include? '.lproj'
346
362
  next if name == '.'
347
363
  # Make sure groups have the correct absolute path set, as intermittent
348
364
  # directories may not be included in the group structure
349
365
  path += name
350
- group = group[name] || group.new_group(name, path)
366
+ group = group.children.find { |c| c.display_name == name } || group.new_group(name, path)
351
367
  end
352
368
  end
353
369
 
354
370
  # Turn files inside .lproj directories into a variant group
355
- if relative_dir.basename.to_s =~ lproj_regex
371
+ if relative_dir.basename.to_s.downcase.include? '.lproj'
356
372
  group_name = variant_group_name(absolute_pathname)
357
373
  lproj_parent_dir = absolute_pathname.dirname.dirname
358
- group = @variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||
359
- group.new_variant_group(group_name, lproj_parent_dir)
360
- @variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||= group
374
+ group = @variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||=
375
+ group.new_variant_group(group_name, lproj_parent_dir)
361
376
  end
362
377
 
363
378
  group
@@ -20,14 +20,19 @@ module Pod
20
20
  #
21
21
  attr_reader :spec
22
22
 
23
+ # @return [Source] the spec repo source the specification came from
24
+ #
25
+ attr_reader :source
26
+
23
27
  # @return [Bool] whether this resolved specification is only used by tests.
24
28
  #
25
29
  attr_reader :used_by_tests_only
26
30
  alias used_by_tests_only? used_by_tests_only
27
31
 
28
- def initialize(spec, used_by_tests_only)
32
+ def initialize(spec, used_by_tests_only, source)
29
33
  @spec = spec
30
34
  @used_by_tests_only = used_by_tests_only
35
+ @source = source
31
36
  end
32
37
 
33
38
  def name
@@ -66,11 +71,11 @@ module Pod
66
71
  # @return [Array<Source>] The list of the sources which will be used for
67
72
  # the resolution.
68
73
  #
69
- attr_accessor :sources
74
+ attr_reader :sources
70
75
 
71
76
  # @return [Bool] Whether the resolver has sources repositories up-to-date.
72
77
  #
73
- attr_accessor :specs_updated
78
+ attr_reader :specs_updated
74
79
  alias specs_updated? specs_updated
75
80
 
76
81
  # Init a new Resolver
@@ -79,12 +84,18 @@ module Pod
79
84
  # @param [Podfile] podfile @see podfile
80
85
  # @param [Array<Dependency>] locked_dependencies @see locked_dependencies
81
86
  # @param [Array<Source>, Source] sources @see sources
87
+ # @param [Boolean] specs_updated @see specs_updated
88
+ # @param [PodfileDependencyCache] podfile_dependency_cache the podfile dependency cache to use
89
+ # within this Resolver.
82
90
  #
83
- def initialize(sandbox, podfile, locked_dependencies, sources)
91
+ def initialize(sandbox, podfile, locked_dependencies, sources, specs_updated,
92
+ podfile_dependency_cache: Installer::Analyzer::PodfileDependencyCache.from_podfile(podfile))
84
93
  @sandbox = sandbox
85
94
  @podfile = podfile
86
95
  @locked_dependencies = locked_dependencies
87
96
  @sources = Array(sources)
97
+ @specs_updated = specs_updated
98
+ @podfile_dependency_cache = podfile_dependency_cache
88
99
  @platforms_by_dependency = Hash.new { |h, k| h[k] = [] }
89
100
  @cached_sets = {}
90
101
  end
@@ -102,11 +113,13 @@ module Pod
102
113
  # definition.
103
114
  #
104
115
  def resolve
105
- dependencies = podfile.target_definition_list.flat_map do |target|
106
- target.dependencies.each do |dep|
107
- @platforms_by_dependency[dep].push(target.platform).uniq! if target.platform
116
+ dependencies = @podfile_dependency_cache.target_definition_list.flat_map do |target|
117
+ @podfile_dependency_cache.target_definition_dependencies(target).each do |dep|
118
+ next unless target.platform
119
+ @platforms_by_dependency[dep].push(target.platform)
108
120
  end
109
121
  end
122
+ @platforms_by_dependency.each_value(&:uniq!)
110
123
  @activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
111
124
  resolver_specs_by_target
112
125
  rescue Molinillo::ResolverError => e
@@ -120,16 +133,22 @@ module Pod
120
133
  #
121
134
  def resolver_specs_by_target
122
135
  @resolver_specs_by_target ||= {}.tap do |resolver_specs_by_target|
123
- podfile.target_definition_list.each do |target|
124
- dependencies = {}
125
- specs = target.dependencies.map(&:name).flat_map do |name|
136
+ dependencies = {}
137
+ @podfile_dependency_cache.target_definition_list.each do |target|
138
+ specs = @podfile_dependency_cache.target_definition_dependencies(target).flat_map do |dep|
139
+ name = dep.name
126
140
  node = @activated.vertex_named(name)
127
141
  (valid_dependencies_for_target_from_node(target, dependencies, node) << node).map { |s| [s, node.payload.test_specification?] }
128
142
  end
129
143
 
130
144
  resolver_specs_by_target[target] = specs.
131
145
  group_by(&:first).
132
- map { |vertex, spec_test_only_tuples| ResolverSpecification.new(vertex.payload, spec_test_only_tuples.map { |tuple| tuple[1] }.all?) }.
146
+ map do |vertex, spec_test_only_tuples|
147
+ test_only = spec_test_only_tuples.all? { |tuple| tuple[1] }
148
+ payload = vertex.payload
149
+ spec_source = payload.respond_to?(:spec_source) && payload.spec_source
150
+ ResolverSpecification.new(payload, test_only, spec_source)
151
+ end.
133
152
  sort_by(&:name)
134
153
  end
135
154
  end
@@ -153,7 +172,9 @@ module Pod
153
172
  def search_for(dependency)
154
173
  @search ||= {}
155
174
  @search[dependency] ||= begin
156
- specifications_for_dependency(dependency, [requirement_for_locked_pod_named(dependency.name)])
175
+ locked_requirement = requirement_for_locked_pod_named(dependency.name)
176
+ additional_requirements = Array(locked_requirement)
177
+ specifications_for_dependency(dependency, additional_requirements)
157
178
  end
158
179
  @search[dependency].dup
159
180
  end
@@ -327,7 +348,7 @@ module Pod
327
348
  # one Pod installation, so different version of the same Pods for
328
349
  # target definitions are not allowed.
329
350
  #
330
- attr_accessor :cached_sets
351
+ attr_reader :cached_sets
331
352
 
332
353
  #-------------------------------------------------------------------------#
333
354
 
@@ -347,7 +368,7 @@ module Pod
347
368
  # @return [Array<Specification>] List of specifications satisfying given requirements.
348
369
  #
349
370
  def specifications_for_dependency(dependency, additional_requirements = [])
350
- requirement = Requirement.new(dependency.requirement.as_list + additional_requirements)
371
+ requirement = Requirement.new(dependency.requirement.as_list + additional_requirements.flat_map(&:as_list))
351
372
  find_cached_set(dependency).
352
373
  all_specifications(installation_options.warn_for_multiple_pod_sources).
353
374
  select { |s| requirement.satisfied_by? s.version }.
@@ -477,7 +498,7 @@ module Pod
477
498
  # Conflict was caused by not specifying an explicit version for the requirement #[name],
478
499
  # and there is no available stable version satisfying constraints for the requirement.
479
500
  o << "\nThere are only pre-release versions available satisfying the following requirements:\n"
480
- conflict.requirements.values.flatten.each do |r|
501
+ conflict.requirements.values.flatten.uniq.each do |r|
481
502
  unless search_for(r).empty?
482
503
  o << "\n\t'#{name}', '#{r.requirement}'\n"
483
504
  end
@@ -525,11 +546,15 @@ module Pod
525
546
  vertex = dependency_graph.vertex_named(dependency.name)
526
547
  predecessors = vertex.recursive_predecessors.select(&:root)
527
548
  predecessors << vertex if vertex.root?
528
- platforms_to_satisfy = predecessors.flat_map(&:explicit_requirements).flat_map { |r| @platforms_by_dependency[r] }
549
+ platforms_to_satisfy = predecessors.flat_map(&:explicit_requirements).flat_map { |r| @platforms_by_dependency[r] }.uniq
550
+
551
+ available_platforms = spec.available_platforms
529
552
 
530
553
  platforms_to_satisfy.all? do |platform_to_satisfy|
531
- spec.available_platforms.select { |spec_platform| spec_platform.name == platform_to_satisfy.name }.
532
- all? { |spec_platform| platform_to_satisfy.supports?(spec_platform) }
554
+ available_platforms.all? do |spec_platform|
555
+ next true unless spec_platform.name == platform_to_satisfy.name
556
+ platform_to_satisfy.supports?(spec_platform)
557
+ end
533
558
  end
534
559
  end
535
560
 
@@ -542,28 +567,31 @@ module Pod
542
567
  # dependencies for `target`.
543
568
  #
544
569
  def valid_dependencies_for_target_from_node(target, dependencies, node)
545
- dependencies[node.name] ||= begin
570
+ dependencies[[node.name, target.platform]] ||= begin
546
571
  validate_platform(node.payload, target)
547
- dependency_nodes = node.outgoing_edges.select do |edge|
548
- edge_is_valid_for_target?(edge, target)
549
- end.map(&:destination)
550
-
551
- dependency_nodes + dependency_nodes.flat_map do |item|
552
- node_result = valid_dependencies_for_target_from_node(target, dependencies, item)
553
- node_result
572
+ dependency_nodes = []
573
+ node.outgoing_edges.each do |edge|
574
+ next unless edge_is_valid_for_target_platform?(edge, target.platform)
575
+ dependency_nodes << edge.destination
554
576
  end
577
+
578
+ dependency_nodes.flat_map do |item|
579
+ valid_dependencies_for_target_from_node(target, dependencies, item)
580
+ end.concat dependency_nodes
555
581
  end
556
582
  end
557
583
 
558
584
  # Whether the given `edge` should be followed to find dependencies for the
559
- # given `target`.
585
+ # given `target_platform`.
560
586
  #
561
587
  # @return [Bool]
562
588
  #
563
- def edge_is_valid_for_target?(edge, target)
564
- dependencies_for_target_platform =
565
- edge.origin.payload.all_dependencies(target.platform).map(&:name)
566
- dependencies_for_target_platform.include?(edge.requirement.name)
589
+ def edge_is_valid_for_target_platform?(edge, target_platform)
590
+ requirement_name = edge.requirement.name
591
+
592
+ edge.origin.payload.all_dependencies(target_platform).any? do |dep|
593
+ dep.name == requirement_name
594
+ end
567
595
  end
568
596
  end
569
597
  end
@@ -1,34 +1,44 @@
1
+ require 'delegate'
1
2
  module Pod
2
3
  class Specification
3
4
  class Set
4
- class LazySpecification < BasicObject
5
- attr_reader :name, :version, :source
6
-
7
- def initialize(name, version, source)
8
- @name = name
9
- @version = version
10
- @source = source
5
+ class SpecWithSource < DelegateClass(Specification)
6
+ attr_reader :spec_source
7
+ def initialize(spec, source)
8
+ super(spec)
9
+ @spec_source = source
11
10
  end
12
11
 
13
- def method_missing(method, *args, &block)
14
- specification.send(method, *args, &block)
15
- end
12
+ undef is_a?
13
+ end
14
+
15
+ class LazySpecification < DelegateClass(Specification)
16
+ attr_reader :name, :version, :spec_source
16
17
 
17
- def respond_to_missing?(method, include_all = false)
18
- specification.respond_to?(method, include_all)
18
+ def initialize(name, version, spec_source)
19
+ @name = name
20
+ @version = version
21
+ @spec_source = spec_source
19
22
  end
20
23
 
21
24
  def subspec_by_name(name = nil, raise_if_missing = true, include_test_specifications = false)
22
- if !name || name == self.name
23
- self
24
- else
25
- specification.subspec_by_name(name, raise_if_missing, include_test_specifications)
26
- end
25
+ subspec =
26
+ if !name || name == self.name
27
+ self
28
+ else
29
+ specification.subspec_by_name(name, raise_if_missing, include_test_specifications)
30
+ end
31
+ return unless subspec
32
+
33
+ SpecWithSource.new subspec, spec_source
27
34
  end
28
35
 
29
36
  def specification
30
- @specification ||= source.specification(name, version.version)
37
+ @specification ||= spec_source.specification(name, version.version)
31
38
  end
39
+ alias __getobj__ specification
40
+
41
+ undef is_a?
32
42
  end
33
43
 
34
44
  class External