cocoapods-core 0.33.1 → 0.34.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cocoapods-core/dependency.rb +1 -1
- data/lib/cocoapods-core/gem_version.rb +1 -1
- data/lib/cocoapods-core/http.rb +4 -3
- data/lib/cocoapods-core/lockfile.rb +16 -15
- data/lib/cocoapods-core/podfile/dsl.rb +61 -3
- data/lib/cocoapods-core/podfile/target_definition.rb +100 -1
- data/lib/cocoapods-core/podfile.rb +9 -2
- data/lib/cocoapods-core/requirement.rb +1 -1
- data/lib/cocoapods-core/source/abstract_data_provider.rb +3 -3
- data/lib/cocoapods-core/source/aggregate.rb +17 -31
- data/lib/cocoapods-core/specification/consumer.rb +2 -2
- data/lib/cocoapods-core/specification/dsl/attribute_support.rb +4 -4
- data/lib/cocoapods-core/specification/dsl/deprecations.rb +0 -37
- data/lib/cocoapods-core/specification/dsl.rb +11 -19
- data/lib/cocoapods-core/specification/json.rb +2 -2
- data/lib/cocoapods-core/specification/linter/analyzer.rb +89 -20
- data/lib/cocoapods-core/specification/linter/result.rb +1 -1
- data/lib/cocoapods-core/specification/linter.rb +19 -2
- data/lib/cocoapods-core/specification/root_attribute_accessors.rb +7 -0
- data/lib/cocoapods-core/specification/set/presenter.rb +22 -0
- data/lib/cocoapods-core/specification/set/statistics.rb +3 -3
- data/lib/cocoapods-core/specification/set.rb +1 -1
- data/lib/cocoapods-core/specification.rb +5 -5
- data/lib/cocoapods-core/yaml_helper.rb +72 -11
- data/lib/cocoapods-core.rb +1 -1
- metadata +27 -27
@@ -38,7 +38,6 @@ module Pod
|
|
38
38
|
# spec.source = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' }
|
39
39
|
# spec.source_files = 'Reachability.{h,m}'
|
40
40
|
# spec.framework = 'SystemConfiguration'
|
41
|
-
# spec.requires_arc = true
|
42
41
|
# end
|
43
42
|
#
|
44
43
|
module DSL
|
@@ -179,6 +178,8 @@ module Pod
|
|
179
178
|
# Unless the source contains a file named `LICENSE.*` or `LICENCE.*`,
|
180
179
|
# the path of the license file **or** the integral text of the notice
|
181
180
|
# commonly used for the license type must be specified.
|
181
|
+
# If a license file is specified, it either must be without a file
|
182
|
+
# extensions or be one of `txt`, `md`, or `markdown`.
|
182
183
|
#
|
183
184
|
# This information is used by CocoaPods to generate acknowledgement
|
184
185
|
# files (markdown and plist) which can be used in the acknowledgements
|
@@ -245,17 +246,12 @@ module Pod
|
|
245
246
|
# @example Specifying a Git source with a tag. This is how most OSS Podspecs work.
|
246
247
|
#
|
247
248
|
# spec.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git',
|
248
|
-
# :tag =>
|
249
|
+
# :tag => spec.version.to_s }
|
249
250
|
#
|
250
|
-
# @example Using
|
251
|
+
# @example Using a tag prefixed with 'v' and submodules.
|
251
252
|
#
|
252
|
-
# spec.source = { :git => 'https://github.com/
|
253
|
-
# :
|
254
|
-
#
|
255
|
-
# @example Using the version of the Pod to identify the Git branch.
|
256
|
-
#
|
257
|
-
# spec.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git',
|
258
|
-
# :branch => "orta_fixes"}
|
253
|
+
# spec.source = { :git => 'https://github.com/typhoon-framework/Typhoon.git',
|
254
|
+
# :tag => "v#{spec.version}", :submodules => true }
|
259
255
|
#
|
260
256
|
# @example Using Subversion with a tag.
|
261
257
|
#
|
@@ -525,7 +521,7 @@ module Pod
|
|
525
521
|
# @param [String] args
|
526
522
|
# The deployment target of the platform.
|
527
523
|
#
|
528
|
-
def deployment_target=(*
|
524
|
+
def deployment_target=(*_args)
|
529
525
|
raise Informative, 'The deployment target can be declared only per ' \
|
530
526
|
'platform.'
|
531
527
|
end
|
@@ -599,24 +595,20 @@ module Pod
|
|
599
595
|
|
600
596
|
# @!method requires_arc=(flag)
|
601
597
|
#
|
602
|
-
#
|
598
|
+
# Whether the library requires ARC to be compiled. If true the
|
603
599
|
# `-fobjc-arc` flag will be added to the compiler flags.
|
604
|
-
#
|
605
|
-
# ---
|
606
|
-
#
|
607
|
-
# The default value of this attribute is __transitioning__ from `false`
|
608
|
-
# to `true`, and in the meanwhile this attribute is always required.
|
600
|
+
# The default value of this attribute is __transitioning__ is `true`.
|
609
601
|
#
|
610
602
|
# @example
|
611
603
|
#
|
612
|
-
# spec.requires_arc =
|
604
|
+
# spec.requires_arc = false
|
613
605
|
#
|
614
606
|
# @param [Bool] flag
|
615
607
|
# whether the source files require ARC.
|
616
608
|
#
|
617
609
|
attribute :requires_arc,
|
618
610
|
:types => [TrueClass, FalseClass],
|
619
|
-
:default_value =>
|
611
|
+
:default_value => true,
|
620
612
|
:inherited => true
|
621
613
|
|
622
614
|
#------------------#
|
@@ -5,7 +5,7 @@ module Pod
|
|
5
5
|
#
|
6
6
|
def to_json(*a)
|
7
7
|
require 'json'
|
8
|
-
to_hash.to_json(*a)
|
8
|
+
to_hash.to_json(*a) << "\n"
|
9
9
|
end
|
10
10
|
|
11
11
|
#-----------------------------------------------------------------------#
|
@@ -16,7 +16,7 @@ module Pod
|
|
16
16
|
def to_hash
|
17
17
|
hash = attributes_hash.dup
|
18
18
|
unless subspecs.empty?
|
19
|
-
hash['subspecs'] = subspecs.map
|
19
|
+
hash['subspecs'] = subspecs.map(&:to_hash)
|
20
20
|
end
|
21
21
|
hash
|
22
22
|
end
|
@@ -12,8 +12,8 @@ module Pod
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def analyze
|
15
|
+
check_attributes
|
15
16
|
validate_file_patterns
|
16
|
-
check_tmp_arc_not_nil
|
17
17
|
check_if_spec_is_empty
|
18
18
|
end
|
19
19
|
|
@@ -21,6 +21,46 @@ module Pod
|
|
21
21
|
|
22
22
|
attr_reader :consumer
|
23
23
|
|
24
|
+
# Checks the attributes hash for any unknown key which might be the
|
25
|
+
# result of a misspelling in a JSON file.
|
26
|
+
#
|
27
|
+
# @note Sub-keys are not checked per-platform as
|
28
|
+
# there is no attribute supporting this combination.
|
29
|
+
#
|
30
|
+
# @note The keys of sub-keys are not checked as they are only used by
|
31
|
+
# the `source` attribute and they are subject
|
32
|
+
# to change according to the support in the
|
33
|
+
# `cocoapods-downloader` gem.
|
34
|
+
#
|
35
|
+
def check_attributes
|
36
|
+
attributes_keys = Pod::Specification::DSL.attributes.keys.map(&:to_s)
|
37
|
+
platform_keys = Specification::DSL::PLATFORMS.map(&:to_s)
|
38
|
+
valid_keys = attributes_keys + platform_keys
|
39
|
+
attributes_hash = consumer.spec.attributes_hash
|
40
|
+
keys = attributes_hash.keys
|
41
|
+
Specification::DSL::PLATFORMS.each do |platform|
|
42
|
+
if attributes_hash[platform.to_s]
|
43
|
+
keys += attributes_hash[platform.to_s].keys
|
44
|
+
end
|
45
|
+
end
|
46
|
+
unknown_keys = keys - valid_keys
|
47
|
+
|
48
|
+
unknown_keys.each do |key|
|
49
|
+
warning "Unrecognized `#{key}` key"
|
50
|
+
end
|
51
|
+
|
52
|
+
Pod::Specification::DSL.attributes.each do |_key, attribute|
|
53
|
+
if attribute.keys && attribute.name != :platforms
|
54
|
+
if attribute.root_only?
|
55
|
+
value = consumer.spec.send(attribute.name)
|
56
|
+
else
|
57
|
+
value = consumer.send(attribute.name)
|
58
|
+
end
|
59
|
+
validate_attribute_value(attribute, value) if value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
24
64
|
# Checks the attributes that represent file patterns.
|
25
65
|
#
|
26
66
|
# @todo Check the attributes hash directly.
|
@@ -41,25 +81,6 @@ module Pod
|
|
41
81
|
end
|
42
82
|
end
|
43
83
|
|
44
|
-
# @todo remove after the switch to true
|
45
|
-
#
|
46
|
-
def check_tmp_arc_not_nil
|
47
|
-
spec = consumer.spec
|
48
|
-
declared = false
|
49
|
-
loop do
|
50
|
-
declared = true unless spec.attributes_hash['requires_arc'].nil?
|
51
|
-
declared = true unless spec.attributes_hash[consumer.platform_name.to_s].nil?
|
52
|
-
spec = spec.parent
|
53
|
-
break unless spec
|
54
|
-
end
|
55
|
-
|
56
|
-
unless declared
|
57
|
-
warning '[requires_arc] A value for `requires_arc` should be' \
|
58
|
-
' specified until the ' \
|
59
|
-
'migration to a `true` default.'
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
84
|
# Check empty subspec attributes
|
64
85
|
#
|
65
86
|
def check_if_spec_is_empty
|
@@ -75,6 +96,54 @@ module Pod
|
|
75
96
|
'or subspecs).'
|
76
97
|
end
|
77
98
|
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Validates the given value for the given attribute.
|
103
|
+
#
|
104
|
+
# @param [Spec::DSL::Attribute] attribute
|
105
|
+
# The attribute.
|
106
|
+
#
|
107
|
+
# @param [Spec::DSL::Attribute] value
|
108
|
+
# The value of the attribute.
|
109
|
+
#
|
110
|
+
def validate_attribute_value(attribute, value)
|
111
|
+
if attribute.keys.is_a?(Array)
|
112
|
+
validate_attribute_array_keys(attribute, value)
|
113
|
+
elsif attribute.keys.is_a?(Hash)
|
114
|
+
validate_attribute_hash_keys(attribute, value)
|
115
|
+
else
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def validate_attribute_array_keys(attribute, value)
|
120
|
+
unknown_keys = value.keys.map(&:to_s) - attribute.keys.map(&:to_s)
|
121
|
+
unknown_keys.each do |unknown_key|
|
122
|
+
warning "Unrecognized `#{unknown_key}` key for " \
|
123
|
+
"`#{attribute.name}` attribute"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate_attribute_hash_keys(attribute, value)
|
128
|
+
major_keys = value.keys & attribute.keys.keys
|
129
|
+
if major_keys.count.zero?
|
130
|
+
warning "Missing primary key for `#{attribute.name}` " \
|
131
|
+
'attribute. The acceptable ones are: ' \
|
132
|
+
"`#{attribute.keys.keys.map(&:to_s).sort.join(', ')}`"
|
133
|
+
elsif major_keys.count == 1
|
134
|
+
acceptable = attribute.keys[major_keys.first]
|
135
|
+
unknown = value.keys - major_keys - acceptable
|
136
|
+
unless unknown.empty?
|
137
|
+
warning "Incompatible `#{unknown.sort.join(', ')}` key(s) " \
|
138
|
+
"with `#{major_keys.first}` primary key for " \
|
139
|
+
"`#{attribute.name}` attribute"
|
140
|
+
end
|
141
|
+
else
|
142
|
+
sorted_keys = major_keys.map(&:to_s).sort
|
143
|
+
warning "Incompatible `#{sorted_keys.join(', ')}` keys for " \
|
144
|
+
"`#{attribute.name}` attribute"
|
145
|
+
end
|
146
|
+
end
|
78
147
|
end
|
79
148
|
end
|
80
149
|
end
|
@@ -31,7 +31,7 @@ module Pod
|
|
31
31
|
@file = Pathname.new(spec_or_path)
|
32
32
|
begin
|
33
33
|
@spec = Specification.from_file(@file)
|
34
|
-
rescue
|
34
|
+
rescue => e
|
35
35
|
@spec = nil
|
36
36
|
@raise_message = e.message
|
37
37
|
end
|
@@ -166,7 +166,7 @@ module Pod
|
|
166
166
|
|
167
167
|
# Performs validations related to the `name` attribute.
|
168
168
|
#
|
169
|
-
def _validate_name(
|
169
|
+
def _validate_name(_n)
|
170
170
|
if spec.name && file
|
171
171
|
acceptable_names = [
|
172
172
|
spec.root.name + '.podspec',
|
@@ -273,6 +273,7 @@ module Pod
|
|
273
273
|
#
|
274
274
|
def _validate_license(l)
|
275
275
|
type = l[:type]
|
276
|
+
file = l[:file]
|
276
277
|
if type.nil?
|
277
278
|
warning '[license] Missing license type.'
|
278
279
|
end
|
@@ -282,6 +283,9 @@ module Pod
|
|
282
283
|
if type && type =~ /\(example\)/
|
283
284
|
error '[license] Sample license type.'
|
284
285
|
end
|
286
|
+
if file && Pathname.new(file).extname !~ /^(txt|md|markdown|)$/i
|
287
|
+
error '[license] Invalid file type'
|
288
|
+
end
|
285
289
|
end
|
286
290
|
|
287
291
|
# Performs validations related to the `source` attribute.
|
@@ -311,6 +315,7 @@ module Pod
|
|
311
315
|
end
|
312
316
|
|
313
317
|
perform_github_source_checks(s)
|
318
|
+
check_git_ssh_source(s)
|
314
319
|
end
|
315
320
|
|
316
321
|
# Performs validations related to github sources.
|
@@ -338,6 +343,18 @@ module Pod
|
|
338
343
|
end
|
339
344
|
end
|
340
345
|
|
346
|
+
# Performs validations related to SSH sources
|
347
|
+
#
|
348
|
+
def check_git_ssh_source(s)
|
349
|
+
if git = s[:git]
|
350
|
+
if git =~ /\w+\@(\w|\.)+\:(\/\w+)*/
|
351
|
+
warning '[source] Git SSH URLs will NOT work for people behind' \
|
352
|
+
'firewalls configured to only allow HTTP, therefor HTTPS is' \
|
353
|
+
'preferred.'
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
341
358
|
# Performs validations related to the `social_media_url` attribute.
|
342
359
|
#
|
343
360
|
def _validate_social_media_url(s)
|
@@ -158,6 +158,13 @@ module Pod
|
|
158
158
|
attributes_hash['deprecated_in_favor_of']
|
159
159
|
end
|
160
160
|
|
161
|
+
# @return [Bool] Wether the pod is deprecated either in favor of some other
|
162
|
+
# pod or simply deprecated.
|
163
|
+
#
|
164
|
+
def deprecated?
|
165
|
+
deprecated || !deprecated_in_favor_of.nil?
|
166
|
+
end
|
167
|
+
|
161
168
|
#---------------------------------------------------------------------#
|
162
169
|
|
163
170
|
private
|
@@ -123,6 +123,28 @@ module Pod
|
|
123
123
|
spec.description || spec.summary
|
124
124
|
end
|
125
125
|
|
126
|
+
# @return [String] A string that describes the deprecation of the pod.
|
127
|
+
# If the pod is deprecated in favor of another pod it will contain
|
128
|
+
# information about that. If the pod is not deprecated returns nil.
|
129
|
+
#
|
130
|
+
# @example Output example
|
131
|
+
#
|
132
|
+
# "[DEPRECATED]"
|
133
|
+
# "[DEPRECATED in favor of NewAwesomePod]"
|
134
|
+
#
|
135
|
+
def deprecation_description
|
136
|
+
if spec.deprecated?
|
137
|
+
description = '[DEPRECATED'
|
138
|
+
if spec.deprecated_in_favor_of.nil?
|
139
|
+
description += ']'
|
140
|
+
else
|
141
|
+
description += " in favor of #{spec.deprecated_in_favor_of}]"
|
142
|
+
end
|
143
|
+
|
144
|
+
description
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
126
148
|
# @return [String] the URL of the source of the Pod.
|
127
149
|
#
|
128
150
|
def source_url
|
@@ -27,8 +27,8 @@ module Pod
|
|
27
27
|
#
|
28
28
|
# @return [Statistics] the shared statistics instance.
|
29
29
|
#
|
30
|
-
|
31
|
-
|
30
|
+
class << self
|
31
|
+
attr_writer :instance
|
32
32
|
end
|
33
33
|
|
34
34
|
# @return [Pathname] the path to the optional cache file.
|
@@ -153,7 +153,7 @@ module Pod
|
|
153
153
|
def cache
|
154
154
|
unless @cache
|
155
155
|
if cache_file && cache_file.exist?
|
156
|
-
@cache = YAMLHelper.
|
156
|
+
@cache = YAMLHelper.load_string(cache_file.read)
|
157
157
|
else
|
158
158
|
@cache = {}
|
159
159
|
end
|
@@ -94,7 +94,7 @@ module Pod
|
|
94
94
|
|
95
95
|
# TODO
|
96
96
|
#
|
97
|
-
def specification_path_for_version(
|
97
|
+
def specification_path_for_version(_version)
|
98
98
|
sources = []
|
99
99
|
versions_by_source.each do |source, source_versions|
|
100
100
|
sources << source if source_versions.include?(required_version)
|
@@ -120,12 +120,12 @@ module Pod
|
|
120
120
|
# pod.
|
121
121
|
#
|
122
122
|
def self.name_and_version_from_string(string_representation)
|
123
|
-
match_data = string_representation.match(
|
123
|
+
match_data = string_representation.match(/\A(\S*)(?: \((.+)\))?\Z/)
|
124
124
|
unless match_data
|
125
125
|
raise Informative, 'Invalid string representation for a ' \
|
126
|
-
"
|
127
|
-
'
|
128
|
-
'the version of
|
126
|
+
"specification: `#{string_representation}`. " \
|
127
|
+
'The string representation should include the name and ' \
|
128
|
+
'optionally the version of the Pod.'
|
129
129
|
end
|
130
130
|
name = match_data[1]
|
131
131
|
vers = Version.new(match_data[2])
|
@@ -563,7 +563,7 @@ module Pod
|
|
563
563
|
# rubocop:disable Eval
|
564
564
|
eval(string, nil, path.to_s)
|
565
565
|
# rubocop:enable Eval
|
566
|
-
rescue
|
566
|
+
rescue => e
|
567
567
|
message = "Invalid `#{path.basename}` file: #{e.message}"
|
568
568
|
raise DSLError.new(message, path, e.backtrace)
|
569
569
|
end
|
@@ -17,14 +17,14 @@ module Pod
|
|
17
17
|
class YAMLHelper
|
18
18
|
class << self
|
19
19
|
# Returns the YAML representation of the given object. If the given object
|
20
|
-
# is
|
20
|
+
# is a Hash, it accepts an optional hint for sorting the keys.
|
21
21
|
#
|
22
22
|
# @param [String, Symbol, Array, Hash] object
|
23
23
|
# the object to convert
|
24
24
|
#
|
25
25
|
# @param [Array] hash_keys_hint
|
26
26
|
# an array to use as a hint for sorting the keys of the object to
|
27
|
-
# convert if it is
|
27
|
+
# convert if it is a hash.
|
28
28
|
#
|
29
29
|
# @return [String] the YAML representation of the given object.
|
30
30
|
#
|
@@ -38,18 +38,39 @@ module Pod
|
|
38
38
|
result << "\n"
|
39
39
|
end
|
40
40
|
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
41
|
+
# Loads a YAML string and provide more informative
|
42
|
+
# error messages in special cases like merge conflict.
|
43
|
+
#
|
44
|
+
# @param [String] yaml_string
|
45
|
+
# The YAML String to be loaded
|
46
|
+
#
|
47
|
+
# @param [Pathname] file_path
|
48
|
+
# The (optional) file path to be used for read for the YAML file
|
49
|
+
#
|
50
|
+
# @return [Hash, Array] the Ruby YAML representaton
|
51
|
+
#
|
52
|
+
def load_string(yaml_string, file_path = nil)
|
44
53
|
YAML.load(yaml_string)
|
45
|
-
rescue
|
46
|
-
if yaml_has_merge_error(yaml_string)
|
47
|
-
raise Informative,
|
54
|
+
rescue
|
55
|
+
if yaml_has_merge_error?(yaml_string)
|
56
|
+
raise Informative, yaml_merge_conflict_msg(yaml_string, file_path)
|
48
57
|
else
|
49
|
-
raise
|
58
|
+
raise Informative, yaml_parsing_error_msg(yaml_string, file_path)
|
50
59
|
end
|
51
60
|
end
|
52
61
|
|
62
|
+
# Loads a YAML file and leans on the #load_string imp
|
63
|
+
# to do error detection
|
64
|
+
#
|
65
|
+
# @param [Pathname] file_path
|
66
|
+
# The file path to be used for read for the YAML file
|
67
|
+
#
|
68
|
+
# @return [Hash, Array] the Ruby YAML representaton
|
69
|
+
#
|
70
|
+
def load_file(file_path)
|
71
|
+
load_string(File.read(file_path), file_path)
|
72
|
+
end
|
73
|
+
|
53
74
|
#-----------------------------------------------------------------------#
|
54
75
|
|
55
76
|
private
|
@@ -130,12 +151,52 @@ module Pod
|
|
130
151
|
end
|
131
152
|
|
132
153
|
# Check for merge errors in a YAML string.
|
133
|
-
#
|
154
|
+
#
|
155
|
+
# @param [String] yaml_string
|
156
|
+
# A YAML string to evaluate
|
157
|
+
#
|
134
158
|
# @return If a merge error was detected or not.
|
135
|
-
|
159
|
+
#
|
160
|
+
def yaml_has_merge_error?(yaml_string)
|
136
161
|
yaml_string.include?('<<<<<<< HEAD')
|
137
162
|
end
|
138
163
|
|
164
|
+
# Error message describing that a merge conflict was found
|
165
|
+
# while parsing the YAML.
|
166
|
+
#
|
167
|
+
# @param [String] yaml
|
168
|
+
# Offending YAML
|
169
|
+
#
|
170
|
+
# @param [Pathname] path
|
171
|
+
# The (optional) offending path
|
172
|
+
#
|
173
|
+
# @return [String] The Error Message
|
174
|
+
#
|
175
|
+
def yaml_merge_conflict_msg(yaml, path = nil)
|
176
|
+
err = 'ERROR: Parsing unable to continue due '
|
177
|
+
err += "to merge conflicts present in:\n"
|
178
|
+
err += "the file located at #{path}\n" if path
|
179
|
+
err += "#{yaml}"
|
180
|
+
end
|
181
|
+
|
182
|
+
# Error message describing a general error took happened
|
183
|
+
# while parsing the YAML.
|
184
|
+
#
|
185
|
+
# @param [String] yaml
|
186
|
+
# Offending YAML
|
187
|
+
#
|
188
|
+
# @param [Pathname] path
|
189
|
+
# The (optional) offending path
|
190
|
+
#
|
191
|
+
# @return [String] The Error Message
|
192
|
+
#
|
193
|
+
def yaml_parsing_error_msg(yaml, path = nil)
|
194
|
+
err = 'ERROR: Parsing unable to continue due '
|
195
|
+
err += "to parsing error:\n"
|
196
|
+
err += "contained in the file located at #{path}\n" if path
|
197
|
+
err += "#{yaml}"
|
198
|
+
end
|
199
|
+
|
139
200
|
#-----------------------------------------------------------------------#
|
140
201
|
|
141
202
|
private
|