cocoapods-core 0.30.0 → 1.15.2
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 +5 -5
- data/README.md +7 -10
- data/lib/cocoapods-core/build_type.rb +121 -0
- data/lib/cocoapods-core/cdn_source.rb +501 -0
- data/lib/cocoapods-core/core_ui.rb +4 -3
- data/lib/cocoapods-core/dependency.rb +100 -73
- data/lib/cocoapods-core/gem_version.rb +1 -2
- data/lib/cocoapods-core/github.rb +32 -5
- data/lib/cocoapods-core/http.rb +86 -0
- data/lib/cocoapods-core/lockfile.rb +161 -56
- data/lib/cocoapods-core/metrics.rb +47 -0
- data/lib/cocoapods-core/platform.rb +99 -11
- data/lib/cocoapods-core/podfile/dsl.rb +623 -124
- data/lib/cocoapods-core/podfile/target_definition.rb +662 -109
- data/lib/cocoapods-core/podfile.rb +138 -65
- data/lib/cocoapods-core/requirement.rb +37 -8
- data/lib/cocoapods-core/source/acceptor.rb +16 -13
- data/lib/cocoapods-core/source/aggregate.rb +79 -103
- data/lib/cocoapods-core/source/health_reporter.rb +9 -18
- data/lib/cocoapods-core/source/manager.rb +488 -0
- data/lib/cocoapods-core/source/metadata.rb +79 -0
- data/lib/cocoapods-core/source.rb +241 -70
- data/lib/cocoapods-core/specification/consumer.rb +187 -47
- data/lib/cocoapods-core/specification/dsl/attribute.rb +49 -85
- data/lib/cocoapods-core/specification/dsl/attribute_support.rb +6 -8
- data/lib/cocoapods-core/specification/dsl/deprecations.rb +9 -126
- data/lib/cocoapods-core/specification/dsl/platform_proxy.rb +30 -20
- data/lib/cocoapods-core/specification/dsl.rb +943 -296
- data/lib/cocoapods-core/specification/json.rb +64 -23
- data/lib/cocoapods-core/specification/linter/analyzer.rb +218 -0
- data/lib/cocoapods-core/specification/linter/result.rb +128 -0
- data/lib/cocoapods-core/specification/linter.rb +310 -309
- data/lib/cocoapods-core/specification/root_attribute_accessors.rb +90 -39
- data/lib/cocoapods-core/specification/set/presenter.rb +35 -71
- data/lib/cocoapods-core/specification/set.rb +42 -96
- data/lib/cocoapods-core/specification.rb +368 -130
- data/lib/cocoapods-core/standard_error.rb +45 -24
- data/lib/cocoapods-core/trunk_source.rb +14 -0
- data/lib/cocoapods-core/vendor/requirement.rb +133 -53
- data/lib/cocoapods-core/vendor/version.rb +197 -156
- data/lib/cocoapods-core/vendor.rb +1 -5
- data/lib/cocoapods-core/version.rb +137 -42
- data/lib/cocoapods-core/yaml_helper.rb +334 -0
- data/lib/cocoapods-core.rb +10 -4
- metadata +100 -27
- data/lib/cocoapods-core/source/abstract_data_provider.rb +0 -71
- data/lib/cocoapods-core/source/file_system_data_provider.rb +0 -150
- data/lib/cocoapods-core/source/github_data_provider.rb +0 -143
- data/lib/cocoapods-core/specification/set/statistics.rb +0 -266
- data/lib/cocoapods-core/yaml_converter.rb +0 -192
@@ -1,12 +1,18 @@
|
|
1
1
|
module Pod
|
2
2
|
class Specification
|
3
3
|
module JSONSupport
|
4
|
-
|
5
4
|
# @return [String] the json representation of the specification.
|
6
5
|
#
|
7
6
|
def to_json(*a)
|
8
7
|
require 'json'
|
9
|
-
|
8
|
+
JSON.dump(to_hash, *a) << "\n"
|
9
|
+
end
|
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"
|
10
16
|
end
|
11
17
|
|
12
18
|
#-----------------------------------------------------------------------#
|
@@ -16,17 +22,29 @@ module Pod
|
|
16
22
|
#
|
17
23
|
def to_hash
|
18
24
|
hash = attributes_hash.dup
|
19
|
-
|
20
|
-
|
25
|
+
if root? || available_platforms != parent.available_platforms
|
26
|
+
platforms = Hash[available_platforms.map { |p| [p.name.to_s, p.deployment_target && p.deployment_target.to_s] }]
|
27
|
+
hash['platforms'] = platforms
|
21
28
|
end
|
22
|
-
|
23
|
-
|
29
|
+
specs_by_type = subspecs.group_by(&:spec_type)
|
30
|
+
all_appspecs = specs_by_type[:app] || []
|
31
|
+
all_testspecs = specs_by_type[:test] || []
|
32
|
+
all_subspecs = specs_by_type[:library] || []
|
24
33
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
hash.delete('testspecs')
|
35
|
+
hash['testspecs'] = all_testspecs.map(&:to_hash) unless all_testspecs.empty?
|
36
|
+
hash.delete('appspecs')
|
37
|
+
hash['appspecs'] = all_appspecs.map(&:to_hash) unless all_appspecs.empty?
|
38
|
+
hash.delete('subspecs')
|
39
|
+
hash['subspecs'] = all_subspecs.map(&:to_hash) unless all_subspecs.empty?
|
40
|
+
|
41
|
+
# Since CocoaPods 1.7 version the DSL has changed to be pluralized. When we serialize a podspec to JSON with
|
42
|
+
# 1.7, ensure that we also include the singular version in the hash to maintain backwards compatibility with
|
43
|
+
# < 1.7 versions. We also delete this key and re-add it to ensure it gets added at the end.
|
44
|
+
hash.delete('swift_version')
|
45
|
+
hash['swift_version'] = swift_version.to_s unless swift_version.nil?
|
46
|
+
|
47
|
+
hash
|
30
48
|
end
|
31
49
|
end
|
32
50
|
|
@@ -38,33 +56,56 @@ module Pod
|
|
38
56
|
#
|
39
57
|
# @return [Specification] the specification
|
40
58
|
#
|
41
|
-
def self.from_json(json)
|
59
|
+
def self.from_json(json, path="")
|
42
60
|
require 'json'
|
43
|
-
|
44
|
-
|
61
|
+
begin
|
62
|
+
hash = JSON.parse(json)
|
63
|
+
from_hash(hash)
|
64
|
+
rescue JSON::ParserError => e
|
65
|
+
if path != ""
|
66
|
+
raise e.class, "Failed to parse JSON at file: '#{path}'.\n\n#{e.message}"
|
67
|
+
else raise
|
68
|
+
end
|
69
|
+
end
|
45
70
|
end
|
46
71
|
|
47
72
|
# Configures a new specification from the given hash.
|
48
73
|
#
|
49
|
-
# @param [Hash] the hash which contains the information of the
|
74
|
+
# @param [Hash] hash the hash which contains the information of the
|
50
75
|
# specification.
|
51
76
|
#
|
77
|
+
# @param [Specification] parent the parent of the specification unless the
|
78
|
+
# specification is a root.
|
79
|
+
#
|
52
80
|
# @return [Specification] the specification
|
53
81
|
#
|
54
|
-
def self.from_hash(hash, parent = nil)
|
55
|
-
spec = Spec.new(parent)
|
82
|
+
def self.from_hash(hash, parent = nil, test_specification: false, app_specification: false)
|
56
83
|
attributes_hash = hash.dup
|
84
|
+
spec = Spec.new(parent, nil, test_specification, :app_specification => app_specification)
|
57
85
|
subspecs = attributes_hash.delete('subspecs')
|
86
|
+
testspecs = attributes_hash.delete('testspecs')
|
87
|
+
appspecs = attributes_hash.delete('appspecs')
|
88
|
+
|
89
|
+
## backwards compatibility with 1.3.0
|
90
|
+
spec.test_specification = !attributes_hash['test_type'].nil?
|
91
|
+
|
58
92
|
spec.attributes_hash = attributes_hash
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
93
|
+
spec.subspecs.concat(subspecs_from_hash(spec, subspecs, false, false))
|
94
|
+
spec.subspecs.concat(subspecs_from_hash(spec, testspecs, true, false))
|
95
|
+
spec.subspecs.concat(subspecs_from_hash(spec, appspecs, false, true))
|
96
|
+
|
64
97
|
spec
|
65
98
|
end
|
66
99
|
|
67
|
-
|
100
|
+
def self.subspecs_from_hash(spec, subspecs, test_specification, app_specification)
|
101
|
+
return [] if subspecs.nil?
|
102
|
+
subspecs.map do |s_hash|
|
103
|
+
Specification.from_hash(s_hash, spec,
|
104
|
+
:test_specification => test_specification,
|
105
|
+
:app_specification => app_specification)
|
106
|
+
end
|
107
|
+
end
|
68
108
|
|
109
|
+
#-----------------------------------------------------------------------#
|
69
110
|
end
|
70
111
|
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
require 'cocoapods-core/specification/linter/result'
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
class Specification
|
5
|
+
class Linter
|
6
|
+
class Analyzer
|
7
|
+
def initialize(consumer, results)
|
8
|
+
@consumer = consumer
|
9
|
+
@results = results
|
10
|
+
@results.consumer = @consumer
|
11
|
+
end
|
12
|
+
|
13
|
+
# Analyzes the consumer adding a {Result} for any failed check to
|
14
|
+
# the {#results} object.
|
15
|
+
#
|
16
|
+
# @return [Results] the results of the analysis.
|
17
|
+
#
|
18
|
+
def analyze
|
19
|
+
check_attributes
|
20
|
+
validate_file_patterns
|
21
|
+
check_if_spec_is_empty
|
22
|
+
results
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :consumer
|
28
|
+
|
29
|
+
attr_reader :results
|
30
|
+
|
31
|
+
# @return [Array<String>] Keys that are valid but have been deprecated.
|
32
|
+
#
|
33
|
+
DEPRECATED_KEYS = ['swift_version'].freeze
|
34
|
+
|
35
|
+
# @return [Array<String>] Keys that are only used for internal purposes.
|
36
|
+
#
|
37
|
+
INTERNAL_KEYS = ['configuration_pod_whitelist'].freeze
|
38
|
+
|
39
|
+
# Checks the attributes hash for any unknown key which might be the
|
40
|
+
# result of a misspelling in a JSON file.
|
41
|
+
#
|
42
|
+
# @note Sub-keys are not checked per-platform as
|
43
|
+
# there is no attribute supporting this combination.
|
44
|
+
#
|
45
|
+
# @note The keys of sub-keys are not checked as they are only used by
|
46
|
+
# the `source` attribute and they are subject
|
47
|
+
# to change according to the support in the
|
48
|
+
# `cocoapods-downloader` gem.
|
49
|
+
#
|
50
|
+
def check_attributes
|
51
|
+
attributes_keys = Pod::Specification::DSL.attributes.keys.map(&:to_s)
|
52
|
+
platform_keys = Specification::DSL::PLATFORMS.map(&:to_s)
|
53
|
+
valid_keys = attributes_keys + platform_keys + DEPRECATED_KEYS + INTERNAL_KEYS
|
54
|
+
attributes_hash = consumer.spec.attributes_hash
|
55
|
+
keys = attributes_hash.keys
|
56
|
+
Specification::DSL::PLATFORMS.each do |platform|
|
57
|
+
if attributes_hash[platform.to_s]
|
58
|
+
keys += attributes_hash[platform.to_s].keys
|
59
|
+
end
|
60
|
+
end
|
61
|
+
unknown_keys = keys - valid_keys
|
62
|
+
|
63
|
+
unknown_keys.each do |key|
|
64
|
+
results.add_warning('attributes', "Unrecognized `#{key}` key.")
|
65
|
+
end
|
66
|
+
|
67
|
+
Pod::Specification::DSL.attributes.each do |_key, attribute|
|
68
|
+
declared_value = consumer.spec.attributes_hash[attribute.name.to_s]
|
69
|
+
validate_attribute_occurrence(attribute, declared_value)
|
70
|
+
validate_attribute_type(attribute, declared_value)
|
71
|
+
if attribute.name != :platforms
|
72
|
+
value = value_for_attribute(attribute)
|
73
|
+
validate_attribute_value(attribute, value) if value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Checks the attributes that represent file patterns.
|
79
|
+
#
|
80
|
+
# @todo Check the attributes hash directly.
|
81
|
+
#
|
82
|
+
def validate_file_patterns
|
83
|
+
attributes = DSL.attributes.values.select(&:file_patterns?)
|
84
|
+
attributes.each do |attrb|
|
85
|
+
patterns = consumer.send(attrb.name)
|
86
|
+
|
87
|
+
if patterns.is_a?(Hash)
|
88
|
+
patterns = patterns.values.flatten(1)
|
89
|
+
end
|
90
|
+
|
91
|
+
if patterns.respond_to?(:each)
|
92
|
+
patterns.each do |pattern|
|
93
|
+
pattern = pattern[:paths].join if attrb.name == :on_demand_resources
|
94
|
+
if pattern.respond_to?(:start_with?) && pattern.start_with?('/')
|
95
|
+
results.add_error('File Patterns', 'File patterns must be ' \
|
96
|
+
"relative and cannot start with a slash (#{attrb.name}).")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Check empty subspec attributes
|
104
|
+
#
|
105
|
+
def check_if_spec_is_empty
|
106
|
+
methods = %w( source_files on_demand_resources resources resource_bundles preserve_paths
|
107
|
+
dependencies vendored_libraries vendored_frameworks )
|
108
|
+
empty_patterns = methods.all? { |m| consumer.send(m).empty? }
|
109
|
+
empty = empty_patterns && consumer.spec.subspecs.empty?
|
110
|
+
if empty
|
111
|
+
results.add_error('File Patterns', "The #{consumer.spec} spec is " \
|
112
|
+
'empty (no source files, resources, resource_bundles, ' \
|
113
|
+
'preserve paths, vendored_libraries, vendored_frameworks, ' \
|
114
|
+
'dependencies, nor subspecs).')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
# Returns the own or inherited (if applicable) value of the
|
121
|
+
# given attribute.
|
122
|
+
#
|
123
|
+
# @param [Spec::DSL::Attribute] attribute
|
124
|
+
# The attribute.
|
125
|
+
#
|
126
|
+
# @return [mixed]
|
127
|
+
#
|
128
|
+
def value_for_attribute(attribute)
|
129
|
+
if attribute.root_only?
|
130
|
+
consumer.spec.send(attribute.name)
|
131
|
+
else
|
132
|
+
consumer.send(attribute.name) if consumer.respond_to?(attribute.name)
|
133
|
+
end
|
134
|
+
rescue => e
|
135
|
+
results.add_error('attributes', "Unable to validate `#{attribute.name}` (#{e}).")
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
# Validates that root attributes don't occur in subspecs.
|
140
|
+
#
|
141
|
+
# @param [Spec::DSL::Attribute] attribute
|
142
|
+
# The attribute.
|
143
|
+
|
144
|
+
# @param [Object] value
|
145
|
+
# The value of the attribute.
|
146
|
+
#
|
147
|
+
def validate_attribute_occurrence(attribute, value)
|
148
|
+
if attribute.root_only? && !value.nil? && !consumer.spec.root?
|
149
|
+
results.add_error('attributes', "Can't set `#{attribute.name}` attribute for " \
|
150
|
+
"subspecs (in `#{consumer.spec.name}`).")
|
151
|
+
end
|
152
|
+
if attribute.test_only? && !value.nil? && !consumer.spec.test_specification?
|
153
|
+
results.add_error('attributes', "Attribute `#{attribute.name}` can only be set " \
|
154
|
+
"within test specs (in `#{consumer.spec.name}`).")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Validates the given value for the given attribute.
|
159
|
+
#
|
160
|
+
# @param [Spec::DSL::Attribute] attribute
|
161
|
+
# The attribute.
|
162
|
+
#
|
163
|
+
# @param [Object] value
|
164
|
+
# The value of the attribute.
|
165
|
+
#
|
166
|
+
def validate_attribute_value(attribute, value)
|
167
|
+
if attribute.keys.is_a?(Array)
|
168
|
+
validate_attribute_array_keys(attribute, value)
|
169
|
+
elsif attribute.keys.is_a?(Hash)
|
170
|
+
validate_attribute_hash_keys(attribute, value)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def validate_attribute_type(attribute, value)
|
175
|
+
return unless value
|
176
|
+
types = attribute.supported_types
|
177
|
+
if types.none? { |klass| value.class == klass }
|
178
|
+
results.add_error('attributes', 'Unacceptable type ' \
|
179
|
+
"`#{value.class}` for `#{attribute.name}`. Allowed values: `#{types.inspect}`.")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def validate_attribute_array_keys(attribute, value)
|
184
|
+
unknown_keys = value.keys.map(&:to_s) - attribute.keys.map(&:to_s)
|
185
|
+
unknown_keys.each do |unknown_key|
|
186
|
+
results.add_warning('keys', "Unrecognized `#{unknown_key}` key for " \
|
187
|
+
"`#{attribute.name}` attribute.")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def validate_attribute_hash_keys(attribute, value)
|
192
|
+
unless value.is_a?(Hash)
|
193
|
+
results.add_error(attribute.name, "Unsupported type `#{value.class}`, expected `Hash`")
|
194
|
+
return
|
195
|
+
end
|
196
|
+
major_keys = value.keys & attribute.keys.keys
|
197
|
+
if major_keys.count.zero?
|
198
|
+
results.add_warning('keys', "Missing primary key for `#{attribute.name}` " \
|
199
|
+
'attribute. The acceptable ones are: ' \
|
200
|
+
"`#{attribute.keys.keys.map(&:to_s).sort.join(', ')}`.")
|
201
|
+
elsif major_keys.count == 1
|
202
|
+
acceptable = attribute.keys[major_keys.first] || []
|
203
|
+
unknown = value.keys - major_keys - acceptable
|
204
|
+
unless unknown.empty?
|
205
|
+
results.add_warning('keys', "Incompatible `#{unknown.sort.join(', ')}` " \
|
206
|
+
"key(s) with `#{major_keys.first}` primary key for " \
|
207
|
+
"`#{attribute.name}` attribute.")
|
208
|
+
end
|
209
|
+
else
|
210
|
+
sorted_keys = major_keys.map(&:to_s).sort
|
211
|
+
results.add_warning('keys', "Incompatible `#{sorted_keys.join(', ')}` " \
|
212
|
+
"keys for `#{attribute.name}` attribute.")
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Pod
|
2
|
+
class Specification
|
3
|
+
class Linter
|
4
|
+
class Results
|
5
|
+
public
|
6
|
+
|
7
|
+
class Result
|
8
|
+
# @return [Symbol] the type of result.
|
9
|
+
#
|
10
|
+
attr_reader :type
|
11
|
+
|
12
|
+
# @return[String] the name of the attribute associated with result.
|
13
|
+
#
|
14
|
+
attr_reader :attribute_name
|
15
|
+
|
16
|
+
# @return [String] the message associated with result.
|
17
|
+
#
|
18
|
+
attr_reader :message
|
19
|
+
|
20
|
+
# @return [Boolean] whether the result only applies to public specs.
|
21
|
+
#
|
22
|
+
attr_reader :public_only
|
23
|
+
alias_method :public_only?, :public_only
|
24
|
+
|
25
|
+
# @param [Symbol] type @see type
|
26
|
+
# @param [String] message @see message
|
27
|
+
#
|
28
|
+
def initialize(type, attribute_name, message, public_only = false)
|
29
|
+
@type = type
|
30
|
+
@attribute_name = attribute_name
|
31
|
+
@message = message
|
32
|
+
@public_only = public_only
|
33
|
+
@platforms = []
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Array<Platform>] the platforms where this result was
|
37
|
+
# generated.
|
38
|
+
#
|
39
|
+
attr_reader :platforms
|
40
|
+
|
41
|
+
# @return [String] a string representation suitable for UI output.
|
42
|
+
#
|
43
|
+
def to_s
|
44
|
+
r = "[#{type.to_s.upcase}] [#{attribute_name}] #{message}"
|
45
|
+
if platforms != Specification::PLATFORMS
|
46
|
+
platforms_names = platforms.uniq.map do |p|
|
47
|
+
Platform.string_name(p)
|
48
|
+
end
|
49
|
+
r << " [#{platforms_names * ' - '}]" unless platforms.empty?
|
50
|
+
end
|
51
|
+
r
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
@results = []
|
57
|
+
@consumer = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
include Enumerable
|
61
|
+
|
62
|
+
def each
|
63
|
+
results.each { |r| yield r }
|
64
|
+
end
|
65
|
+
|
66
|
+
def empty?
|
67
|
+
results.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Specification::Consumer] the current consumer.
|
71
|
+
#
|
72
|
+
attr_accessor :consumer
|
73
|
+
|
74
|
+
# Adds an error result with the given message.
|
75
|
+
#
|
76
|
+
# @param [String] message
|
77
|
+
# The message of the result.
|
78
|
+
#
|
79
|
+
# @return [void]
|
80
|
+
#
|
81
|
+
def add_error(attribute_name, message, public_only = false)
|
82
|
+
add_result(:error, attribute_name, message, public_only)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Adds a warning result with the given message.
|
86
|
+
#
|
87
|
+
# @param [String] message
|
88
|
+
# The message of the result.
|
89
|
+
#
|
90
|
+
# @return [void]
|
91
|
+
#
|
92
|
+
def add_warning(attribute_name, message, public_only = false)
|
93
|
+
add_result(:warning, attribute_name, message, public_only)
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# @return [Array<Result>] all of the generated results.
|
99
|
+
#
|
100
|
+
attr_reader :results
|
101
|
+
|
102
|
+
# Adds a result of the given type with the given message. If there is a
|
103
|
+
# current platform it is added to the result. If a result with the same
|
104
|
+
# type and the same message is already available the current platform is
|
105
|
+
# added to the existing result.
|
106
|
+
#
|
107
|
+
# @param [Symbol] type
|
108
|
+
# The type of the result (`:error`, `:warning`).
|
109
|
+
#
|
110
|
+
# @param [String] message
|
111
|
+
# The message of the result.
|
112
|
+
#
|
113
|
+
# @return [void]
|
114
|
+
#
|
115
|
+
def add_result(type, attribute_name, message, public_only)
|
116
|
+
result = results.find do |r|
|
117
|
+
r.type == type && r.attribute_name == attribute_name && r.message == message && r.public_only? == public_only
|
118
|
+
end
|
119
|
+
unless result
|
120
|
+
result = Result.new(type, attribute_name, message, public_only)
|
121
|
+
results << result
|
122
|
+
end
|
123
|
+
result.platforms << @consumer.platform_name if @consumer
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|