Class: Pod::Specification::Linter::Analyzer

Inherits:
Object
  • Object
show all
Defined in:
lib/cocoapods-core/specification/linter/analyzer.rb

Constant Summary collapse

DEPRECATED_KEYS =

Returns Keys that are valid but have been deprecated.

Returns:

  • (Array<String>)

    Keys that are valid but have been deprecated.

['swift_version'].freeze
INTERNAL_KEYS =

Returns Keys that are only used for internal purposes.

Returns:

  • (Array<String>)

    Keys that are only used for internal purposes.

['configuration_pod_whitelist'].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(consumer, results) ⇒ Analyzer

Returns a new instance of Analyzer.



7
8
9
10
11
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 7

def initialize(consumer, results)
  @consumer = consumer
  @results = results
  @results.consumer = @consumer
end

Instance Attribute Details

#consumerObject (readonly, private)

Returns the value of attribute consumer.



27
28
29
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 27

def consumer
  @consumer
end

#resultsObject (readonly, private)

Returns the value of attribute results.



29
30
31
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 29

def results
  @results
end

Instance Method Details

#analyzeResults

Analyzes the consumer adding a Result for any failed check to the #results object.

Returns:

  • (Results)

    the results of the analysis.



18
19
20
21
22
23
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 18

def analyze
  check_attributes
  validate_file_patterns
  check_if_spec_is_empty
  results
end

#check_attributesObject (private)

Note:

Sub-keys are not checked per-platform as there is no attribute supporting this combination.

Note:

The keys of sub-keys are not checked as they are only used by the source attribute and they are subject to change according to the support in the cocoapods-downloader gem.

Checks the attributes hash for any unknown key which might be the result of a misspelling in a JSON file.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 50

def check_attributes
  attributes_keys = Pod::Specification::DSL.attributes.keys.map(&:to_s)
  platform_keys = Specification::DSL::PLATFORMS.map(&:to_s)
  valid_keys = attributes_keys + platform_keys + DEPRECATED_KEYS + INTERNAL_KEYS
  attributes_hash = consumer.spec.attributes_hash
  keys = attributes_hash.keys
  Specification::DSL::PLATFORMS.each do |platform|
    if attributes_hash[platform.to_s]
      keys += attributes_hash[platform.to_s].keys
    end
  end
  unknown_keys = keys - valid_keys

  unknown_keys.each do |key|
    results.add_warning('attributes', "Unrecognized `#{key}` key.")
  end

  Pod::Specification::DSL.attributes.each do |_key, attribute|
    declared_value = consumer.spec.attributes_hash[attribute.name.to_s]
    validate_attribute_occurrence(attribute, declared_value)
    validate_attribute_type(attribute, declared_value)
    if attribute.name != :platforms
      value = value_for_attribute(attribute)
      validate_attribute_value(attribute, value) if value
    end
  end
end

#check_if_spec_is_emptyObject (private)

Check empty subspec attributes



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 105

def check_if_spec_is_empty
  methods = %w( source_files on_demand_resources resources resource_bundles preserve_paths
                dependencies vendored_libraries vendored_frameworks )
  empty_patterns = methods.all? { |m| consumer.send(m).empty? }
  empty = empty_patterns && consumer.spec.subspecs.empty?
  if empty
    results.add_error('File Patterns', "The #{consumer.spec} spec is " \
      'empty (no source files, resources, resource_bundles, ' \
      'preserve paths, vendored_libraries, vendored_frameworks, ' \
      'dependencies, nor subspecs).')
  end
end

#validate_attribute_array_keys(attribute, value) ⇒ Object (private)



183
184
185
186
187
188
189
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 183

def validate_attribute_array_keys(attribute, value)
  unknown_keys = value.keys.map(&:to_s) - attribute.keys.map(&:to_s)
  unknown_keys.each do |unknown_key|
    results.add_warning('keys', "Unrecognized `#{unknown_key}` key for " \
      "`#{attribute.name}` attribute.")
  end
end

#validate_attribute_hash_keys(attribute, value) ⇒ Object (private)



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 191

def validate_attribute_hash_keys(attribute, value)
  unless value.is_a?(Hash)
    results.add_error(attribute.name, "Unsupported type `#{value.class}`, expected `Hash`")
    return
  end
  major_keys = value.keys & attribute.keys.keys
  if major_keys.count.zero?
    results.add_warning('keys', "Missing primary key for `#{attribute.name}` " \
      'attribute. The acceptable ones are: ' \
      "`#{attribute.keys.keys.map(&:to_s).sort.join(', ')}`.")
  elsif major_keys.count == 1
    acceptable = attribute.keys[major_keys.first] || []
    unknown = value.keys - major_keys - acceptable
    unless unknown.empty?
      results.add_warning('keys', "Incompatible `#{unknown.sort.join(', ')}` " \
        "key(s) with `#{major_keys.first}` primary key for " \
        "`#{attribute.name}` attribute.")
    end
  else
    sorted_keys = major_keys.map(&:to_s).sort
    results.add_warning('keys', "Incompatible `#{sorted_keys.join(', ')}` " \
      "keys for `#{attribute.name}` attribute.")
  end
end

#validate_attribute_occurrence(attribute, value) ⇒ Object (private)

Parameters:

  • value (Object)

    The value of the attribute.



147
148
149
150
151
152
153
154
155
156
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 147

def validate_attribute_occurrence(attribute, value)
  if attribute.root_only? && !value.nil? && !consumer.spec.root?
    results.add_error('attributes', "Can't set `#{attribute.name}` attribute for " \
      "subspecs (in `#{consumer.spec.name}`).")
  end
  if attribute.test_only? && !value.nil? && !consumer.spec.test_specification?
    results.add_error('attributes', "Attribute `#{attribute.name}` can only be set " \
      "within test specs (in `#{consumer.spec.name}`).")
  end
end

#validate_attribute_type(attribute, value) ⇒ Object (private)



174
175
176
177
178
179
180
181
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 174

def validate_attribute_type(attribute, value)
  return unless value
  types = attribute.supported_types
  if types.none? { |klass| value.class == klass }
    results.add_error('attributes', 'Unacceptable type ' \
      "`#{value.class}` for `#{attribute.name}`. Allowed values: `#{types.inspect}`.")
  end
end

#validate_attribute_value(attribute, value) ⇒ Object (private)

Validates the given value for the given attribute.

Parameters:

  • attribute (Spec::DSL::Attribute)

    The attribute.

  • value (Object)

    The value of the attribute.



166
167
168
169
170
171
172
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 166

def validate_attribute_value(attribute, value)
  if attribute.keys.is_a?(Array)
    validate_attribute_array_keys(attribute, value)
  elsif attribute.keys.is_a?(Hash)
    validate_attribute_hash_keys(attribute, value)
  end
end

#validate_file_patternsObject (private)

TODO:

Check the attributes hash directly.

Checks the attributes that represent file patterns.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 82

def validate_file_patterns
  attributes = DSL.attributes.values.select(&:file_patterns?)
  attributes.each do |attrb|
    patterns = consumer.send(attrb.name)

    if patterns.is_a?(Hash)
      patterns = patterns.values.flatten(1)
    end

    if patterns.respond_to?(:each)
      patterns.each do |pattern|
        pattern = pattern[:paths].join if attrb.name == :on_demand_resources
        if pattern.respond_to?(:start_with?) && pattern.start_with?('/')
          results.add_error('File Patterns', 'File patterns must be ' \
            "relative and cannot start with a slash (#{attrb.name}).")
        end
      end
    end
  end
end

#value_for_attribute(attribute) ⇒ mixed (private)

Returns the own or inherited (if applicable) value of the given attribute.

Parameters:

  • attribute (Spec::DSL::Attribute)

    The attribute.

Returns:

  • (mixed)


128
129
130
131
132
133
134
135
136
137
# File 'lib/cocoapods-core/specification/linter/analyzer.rb', line 128

def value_for_attribute(attribute)
  if attribute.root_only?
    consumer.spec.send(attribute.name)
  else
    consumer.send(attribute.name) if consumer.respond_to?(attribute.name)
  end
rescue => e
  results.add_error('attributes', "Unable to validate `#{attribute.name}` (#{e}).")
  nil
end