xcodeproj 1.7.0 → 1.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/xcodeproj/command/config_dump.rb +5 -1
- data/lib/xcodeproj/config.rb +3 -1
- data/lib/xcodeproj/config/other_linker_flags_parser.rb +2 -2
- data/lib/xcodeproj/constants.rb +70 -44
- data/lib/xcodeproj/gem_version.rb +1 -1
- data/lib/xcodeproj/project.rb +23 -2
- data/lib/xcodeproj/project/object.rb +3 -1
- data/lib/xcodeproj/project/object/build_configuration.rb +96 -35
- data/lib/xcodeproj/project/object/build_file.rb +9 -1
- data/lib/xcodeproj/project/object/build_phase.rb +45 -4
- data/lib/xcodeproj/project/object/build_rule.rb +12 -0
- data/lib/xcodeproj/project/object/helpers/file_references_factory.rb +11 -4
- data/lib/xcodeproj/project/object/helpers/groupable_helper.rb +13 -6
- data/lib/xcodeproj/project/object/native_target.rb +52 -9
- data/lib/xcodeproj/project/object/root_object.rb +15 -2
- data/lib/xcodeproj/project/object/swift_package_product_dependency.rb +19 -0
- data/lib/xcodeproj/project/object/swift_package_remote_reference.rb +19 -0
- data/lib/xcodeproj/project/object/target_dependency.rb +9 -0
- data/lib/xcodeproj/project/project_helper.rb +2 -1
- data/lib/xcodeproj/project/uuid_generator.rb +41 -19
- data/lib/xcodeproj/scheme.rb +6 -4
- data/lib/xcodeproj/scheme/build_action.rb +21 -0
- data/lib/xcodeproj/scheme/buildable_reference.rb +3 -1
- data/lib/xcodeproj/scheme/launch_action.rb +50 -0
- data/lib/xcodeproj/scheme/test_action.rb +36 -1
- data/lib/xcodeproj/workspace.rb +16 -7
- data/lib/xcodeproj/workspace/file_reference.rb +7 -12
- data/lib/xcodeproj/workspace/group_reference.rb +18 -15
- data/lib/xcodeproj/workspace/reference.rb +40 -0
- metadata +11 -9
@@ -224,8 +224,9 @@ module Xcodeproj
|
|
224
224
|
cl.build_configurations << release_conf
|
225
225
|
cl.build_configurations << debug_conf
|
226
226
|
|
227
|
+
existing_configurations = cl.build_configurations.map(&:name)
|
227
228
|
project.build_configurations.each do |configuration|
|
228
|
-
next if
|
229
|
+
next if existing_configurations.include?(configuration.name)
|
229
230
|
|
230
231
|
new_config = project.new(XCBuildConfiguration)
|
231
232
|
new_config.name = configuration.name
|
@@ -3,43 +3,65 @@ module Xcodeproj
|
|
3
3
|
class UUIDGenerator
|
4
4
|
require 'digest'
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
-
@new_objects_by_uuid = {}
|
6
|
+
def initialize(projects)
|
7
|
+
@projects = Array(projects)
|
9
8
|
@paths_by_object = {}
|
10
9
|
end
|
11
10
|
|
12
11
|
def generate!
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@
|
12
|
+
generate_all_paths_by_objects(@projects)
|
13
|
+
|
14
|
+
new_objects_by_project = Hash[@projects.map do |project|
|
15
|
+
[project, switch_uuids(project)]
|
16
|
+
end]
|
17
|
+
all_new_objects_by_project = new_objects_by_project.values.flat_map(&:values)
|
18
|
+
all_objects_by_uuid = @projects.map(&:objects_by_uuid).inject(:merge)
|
19
|
+
all_objects = @projects.flat_map(&:objects)
|
20
|
+
verify_no_duplicates!(all_objects, all_new_objects_by_project)
|
21
|
+
@projects.each { |project| fixup_uuid_references(project, all_objects_by_uuid) }
|
22
|
+
new_objects_by_project.each do |project, new_objects_by_uuid|
|
23
|
+
project.instance_variable_set(:@generated_uuids, project.instance_variable_get(:@available_uuids))
|
24
|
+
project.instance_variable_set(:@objects_by_uuid, new_objects_by_uuid)
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
28
|
private
|
23
29
|
|
24
30
|
UUID_ATTRIBUTES = [:remote_global_id_string, :container_portal, :target_proxy].freeze
|
25
31
|
|
26
|
-
def verify_no_duplicates!(all_objects)
|
27
|
-
duplicates = all_objects -
|
32
|
+
def verify_no_duplicates!(all_objects, all_new_objects)
|
33
|
+
duplicates = all_objects - all_new_objects
|
28
34
|
UserInterface.warn "[Xcodeproj] Generated duplicate UUIDs:\n\n" <<
|
29
35
|
duplicates.map { |d| "#{d.isa} -- #{@paths_by_object[d]}" }.join("\n") unless duplicates.empty?
|
30
36
|
end
|
31
37
|
|
32
|
-
def fixup_uuid_references
|
38
|
+
def fixup_uuid_references(target_project, all_objects_by_uuid)
|
33
39
|
fixup = ->(object, attr) do
|
34
|
-
if object.respond_to?(attr) && link =
|
40
|
+
if object.respond_to?(attr) && link = all_objects_by_uuid[object.send(attr)]
|
35
41
|
object.send(:"#{attr}=", link.uuid)
|
36
42
|
end
|
37
43
|
end
|
38
|
-
|
44
|
+
target_project.objects.each do |object|
|
39
45
|
UUID_ATTRIBUTES.each do |attr|
|
40
46
|
fixup[object, attr]
|
41
47
|
end
|
42
48
|
end
|
49
|
+
|
50
|
+
if (project_attributes = target_project.root_object.attributes) && project_attributes['TargetAttributes']
|
51
|
+
project_attributes['TargetAttributes'] = Hash[project_attributes['TargetAttributes'].map do |target_uuid, attributes|
|
52
|
+
if test_target_id = attributes['TestTargetID']
|
53
|
+
attributes = attributes.merge('TestTargetID' => all_objects_by_uuid[test_target_id].uuid)
|
54
|
+
end
|
55
|
+
if target_object = all_objects_by_uuid[target_uuid]
|
56
|
+
target_uuid = target_object.uuid
|
57
|
+
end
|
58
|
+
[target_uuid, attributes]
|
59
|
+
end]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_all_paths_by_objects(projects)
|
64
|
+
projects.each { |project| generate_paths(project.root_object, project.path.basename.to_s) }
|
43
65
|
end
|
44
66
|
|
45
67
|
def generate_paths(object, path = '')
|
@@ -67,13 +89,13 @@ module Xcodeproj
|
|
67
89
|
end
|
68
90
|
end
|
69
91
|
|
70
|
-
def switch_uuids(
|
71
|
-
|
72
|
-
objects.
|
92
|
+
def switch_uuids(project)
|
93
|
+
project.mark_dirty!
|
94
|
+
project.objects.each_with_object({}) do |object, hash|
|
73
95
|
next unless path = @paths_by_object[object]
|
74
96
|
uuid = uuid_for_path(path)
|
75
97
|
object.instance_variable_set(:@uuid, uuid)
|
76
|
-
|
98
|
+
hash[uuid] = object
|
77
99
|
end
|
78
100
|
end
|
79
101
|
|
data/lib/xcodeproj/scheme.rb
CHANGED
@@ -31,7 +31,9 @@ module Xcodeproj
|
|
31
31
|
def initialize(file_path = nil)
|
32
32
|
if file_path
|
33
33
|
@file_path = file_path
|
34
|
-
@doc =
|
34
|
+
@doc = File.open(file_path, 'r') do |f|
|
35
|
+
REXML::Document.new(f)
|
36
|
+
end
|
35
37
|
@doc.context[:attribute_quote] = :quote
|
36
38
|
|
37
39
|
@scheme = @doc.elements['Scheme']
|
@@ -193,9 +195,9 @@ module Xcodeproj
|
|
193
195
|
|
194
196
|
entry.build_for_testing = true
|
195
197
|
entry.build_for_running = build_for_running
|
196
|
-
entry.build_for_profiling =
|
197
|
-
entry.build_for_archiving =
|
198
|
-
entry.build_for_analyzing =
|
198
|
+
entry.build_for_profiling = build_for_running
|
199
|
+
entry.build_for_archiving = build_for_running
|
200
|
+
entry.build_for_analyzing = build_for_running
|
199
201
|
|
200
202
|
build_action.add_entry(entry)
|
201
203
|
end
|
@@ -59,6 +59,20 @@ module Xcodeproj
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
# @param [Array<BuildAction::Entry>] entries
|
63
|
+
# Sets the list of BuildActionEntry nodes associated with this Build Action.
|
64
|
+
#
|
65
|
+
def entries=(entries)
|
66
|
+
@xml_element.delete_element('BuildActionEntries')
|
67
|
+
unless entries.empty?
|
68
|
+
entries_element = @xml_element.add_element('BuildActionEntries')
|
69
|
+
entries.each do |entry_node|
|
70
|
+
entries_element.add_element(entry_node.xml_element)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
entries
|
74
|
+
end
|
75
|
+
|
62
76
|
# @param [BuildAction::Entry] entry
|
63
77
|
# The BuildActionEntry to add to the list of targets to build for the various actions
|
64
78
|
#
|
@@ -185,6 +199,13 @@ module Xcodeproj
|
|
185
199
|
def add_buildable_reference(ref)
|
186
200
|
@xml_element.add_element(ref.xml_element)
|
187
201
|
end
|
202
|
+
|
203
|
+
# @param [BuildableReference] ref
|
204
|
+
# The BuildableReference to remove from the list of targets this entry will build
|
205
|
+
#
|
206
|
+
def remove_buildable_reference(ref)
|
207
|
+
@xml_element.delete_element(ref.xml_element)
|
208
|
+
end
|
188
209
|
end
|
189
210
|
end
|
190
211
|
end
|
@@ -59,10 +59,12 @@ module Xcodeproj
|
|
59
59
|
# If true, buildable_name will also be updated by computing a name from the target
|
60
60
|
#
|
61
61
|
def set_reference_target(target, override_buildable_name = false, root_project = nil)
|
62
|
+
# note, the order of assignment here is important, it determines the order of serialization in the xml
|
63
|
+
# this matches the order that Xcode generates
|
62
64
|
@xml_element.attributes['BlueprintIdentifier'] = target.uuid
|
65
|
+
self.buildable_name = construct_buildable_name(target) if override_buildable_name
|
63
66
|
@xml_element.attributes['BlueprintName'] = target.name
|
64
67
|
@xml_element.attributes['ReferencedContainer'] = construct_referenced_container_uri(target, root_project)
|
65
|
-
self.buildable_name = construct_buildable_name(target) if override_buildable_name
|
66
68
|
end
|
67
69
|
|
68
70
|
# @return [String]
|
@@ -47,6 +47,40 @@ module Xcodeproj
|
|
47
47
|
@xml_element.attributes['allowLocationSimulation'] = bool_to_string(flag)
|
48
48
|
end
|
49
49
|
|
50
|
+
# @return [Bool]
|
51
|
+
# Whether this Build Action should disable detection of UI API misuse
|
52
|
+
# from background threads
|
53
|
+
#
|
54
|
+
def disable_main_thread_checker?
|
55
|
+
string_to_bool(@xml_element.attributes['disableMainThreadChecker'])
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param [Bool] flag
|
59
|
+
# Set whether this Build Action should disable detection of UI API misuse
|
60
|
+
# from background threads
|
61
|
+
#
|
62
|
+
def disable_main_thread_checker=(flag)
|
63
|
+
@xml_element.attributes['disableMainThreadChecker'] = bool_to_string(flag)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Bool]
|
67
|
+
# Whether UI API misuse from background threads detection should pause execution.
|
68
|
+
# This flag is ignored when the thread checker disabled
|
69
|
+
# ([disable_main_thread_checker] flag).
|
70
|
+
#
|
71
|
+
def stop_on_every_main_thread_checker_issue?
|
72
|
+
string_to_bool(@xml_element.attributes['stopOnEveryMainThreadCheckerIssue'])
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param [Bool] flag
|
76
|
+
# Set whether UI API misuse from background threads detection should pause execution.
|
77
|
+
# This flag is ignored when the thread checker disabled
|
78
|
+
# ([disable_main_thread_checker] flag).
|
79
|
+
#
|
80
|
+
def stop_on_every_main_thread_checker_issue=(flag)
|
81
|
+
@xml_element.attributes['stopOnEveryMainThreadCheckerIssue'] = bool_to_string(flag)
|
82
|
+
end
|
83
|
+
|
50
84
|
# @return [String]
|
51
85
|
# The launch automatically substyle
|
52
86
|
#
|
@@ -109,6 +143,22 @@ module Xcodeproj
|
|
109
143
|
@xml_element.add_element(arguments.xml_element) if arguments
|
110
144
|
arguments
|
111
145
|
end
|
146
|
+
|
147
|
+
# @return [Array<MacroExpansion>]
|
148
|
+
# The list of MacroExpansion bound with this LaunchAction
|
149
|
+
#
|
150
|
+
def macro_expansions
|
151
|
+
@xml_element.get_elements('MacroExpansion').map do |node|
|
152
|
+
MacroExpansion.new(node)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# @param [MacroExpansion] macro_expansion
|
157
|
+
# Add a MacroExpansion to this LaunchAction
|
158
|
+
#
|
159
|
+
def add_macro_expansion(macro_expansion)
|
160
|
+
@xml_element.add_element(macro_expansion.xml_element)
|
161
|
+
end
|
112
162
|
end
|
113
163
|
end
|
114
164
|
end
|
@@ -35,6 +35,22 @@ module Xcodeproj
|
|
35
35
|
@xml_element.attributes['shouldUseLaunchSchemeArgsEnv'] = bool_to_string(flag)
|
36
36
|
end
|
37
37
|
|
38
|
+
# @return [Bool]
|
39
|
+
# Whether this Test Action should disable detection of UI API misuse
|
40
|
+
# from background threads
|
41
|
+
#
|
42
|
+
def disable_main_thread_checker?
|
43
|
+
string_to_bool(@xml_element.attributes['disableMainThreadChecker'])
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [Bool] flag
|
47
|
+
# Set whether this Test Action should disable detection of UI API misuse
|
48
|
+
# from background threads
|
49
|
+
#
|
50
|
+
def disable_main_thread_checker=(flag)
|
51
|
+
@xml_element.attributes['disableMainThreadChecker'] = bool_to_string(flag)
|
52
|
+
end
|
53
|
+
|
38
54
|
# @return [Bool]
|
39
55
|
# Whether Clang Code Coverage is enabled ('Gather coverage data' turned ON)
|
40
56
|
#
|
@@ -42,7 +58,7 @@ module Xcodeproj
|
|
42
58
|
string_to_bool(@xml_element.attributes['codeCoverageEnabled'])
|
43
59
|
end
|
44
60
|
|
45
|
-
# @
|
61
|
+
# @param [Bool] flag
|
46
62
|
# Set whether Clang Code Coverage is enabled ('Gather coverage data' turned ON)
|
47
63
|
#
|
48
64
|
def code_coverage_enabled=(flag)
|
@@ -60,6 +76,18 @@ module Xcodeproj
|
|
60
76
|
end
|
61
77
|
end
|
62
78
|
|
79
|
+
# @param [Array<TestableReference>] testables
|
80
|
+
# Sets the list of TestableReference (test bundles) associated with this Test Action
|
81
|
+
#
|
82
|
+
def testables=(testables)
|
83
|
+
@xml_element.delete_element('Testables')
|
84
|
+
testables_element = @xml_element.add_element('Testables')
|
85
|
+
testables.each do |testable|
|
86
|
+
testables_element.add_element(testable.xml_element)
|
87
|
+
end
|
88
|
+
testables
|
89
|
+
end
|
90
|
+
|
63
91
|
# @param [TestableReference] testable
|
64
92
|
# Add a TestableReference (test bundle) to this Test Action
|
65
93
|
#
|
@@ -203,6 +231,13 @@ module Xcodeproj
|
|
203
231
|
@xml_element.add_element(ref.xml_element)
|
204
232
|
end
|
205
233
|
|
234
|
+
# @param [BuildableReference] ref
|
235
|
+
# The BuildableReference to remove from the list of targets this entry will build
|
236
|
+
#
|
237
|
+
def remove_buildable_reference(ref)
|
238
|
+
@xml_element.delete_element(ref.xml_element)
|
239
|
+
end
|
240
|
+
|
206
241
|
# @return [Array<Test>]
|
207
242
|
# The list of SkippedTest this action will skip.
|
208
243
|
#
|
data/lib/xcodeproj/workspace.rb
CHANGED
@@ -64,7 +64,7 @@ module Xcodeproj
|
|
64
64
|
#
|
65
65
|
def self.new_from_xcworkspace(path)
|
66
66
|
from_s(File.read(File.join(path, 'contents.xcworkspacedata')),
|
67
|
-
File.expand_path(
|
67
|
+
File.expand_path(path))
|
68
68
|
rescue Errno::ENOENT
|
69
69
|
new(nil)
|
70
70
|
end
|
@@ -97,16 +97,19 @@ module Xcodeproj
|
|
97
97
|
#
|
98
98
|
def <<(path_or_reference)
|
99
99
|
return unless @document && @document.respond_to?(:root)
|
100
|
-
|
101
|
-
|
100
|
+
|
101
|
+
case path_or_reference
|
102
|
+
when String
|
102
103
|
project_file_reference = Xcodeproj::Workspace::FileReference.new(path_or_reference)
|
103
|
-
when
|
104
|
+
when Xcodeproj::Workspace::FileReference
|
104
105
|
project_file_reference = path_or_reference
|
105
106
|
projpath = nil
|
106
107
|
else
|
107
|
-
raise ArgumentError,
|
108
|
+
raise ArgumentError, "Input to the << operator must be a file path or FileReference, got #{path_or_reference.inspect}"
|
109
|
+
end
|
110
|
+
unless file_references.any? { |ref| project_file_reference.eql? ref }
|
111
|
+
@document.root.add_element(project_file_reference.to_node)
|
108
112
|
end
|
109
|
-
@document.root.add_element(project_file_reference.to_node)
|
110
113
|
load_schemes_from_project File.expand_path(projpath || project_file_reference.path)
|
111
114
|
end
|
112
115
|
|
@@ -191,8 +194,14 @@ module Xcodeproj
|
|
191
194
|
# @return [void]
|
192
195
|
#
|
193
196
|
def load_schemes(workspace_dir_path)
|
197
|
+
# Normalizes path to directory of workspace needed for file_reference.absolute_path
|
198
|
+
workspaces_dir = workspace_dir_path
|
199
|
+
if File.extname(workspace_dir_path) == '.xcworkspace'
|
200
|
+
workspaces_dir = File.expand_path('..', workspaces_dir)
|
201
|
+
end
|
202
|
+
|
194
203
|
file_references.each do |file_reference|
|
195
|
-
project_full_path = file_reference.absolute_path(
|
204
|
+
project_full_path = file_reference.absolute_path(workspaces_dir)
|
196
205
|
load_schemes_from_project(project_full_path)
|
197
206
|
end
|
198
207
|
|
@@ -1,27 +1,19 @@
|
|
1
|
+
require 'xcodeproj/workspace/reference'
|
2
|
+
|
1
3
|
module Xcodeproj
|
2
4
|
class Workspace
|
3
5
|
# Describes a file reference of a Workspace.
|
4
6
|
#
|
5
|
-
class FileReference
|
7
|
+
class FileReference < Reference
|
6
8
|
# @return [String] the path to the project
|
7
9
|
#
|
8
10
|
attr_reader :path
|
9
11
|
|
10
|
-
# @return [String] the type of reference to the project
|
11
|
-
#
|
12
|
-
# This can be of the following values:
|
13
|
-
# - absolute
|
14
|
-
# - group
|
15
|
-
# - container
|
16
|
-
# - developer (unsupported)
|
17
|
-
#
|
18
|
-
attr_reader :type
|
19
|
-
|
20
12
|
# @param [#to_s] path @see path
|
21
13
|
# @param [#to_s] type @see type
|
22
14
|
#
|
23
15
|
def initialize(path, type = 'group')
|
24
|
-
@path = path.to_s
|
16
|
+
@path = Pathname.new(path.to_s).cleanpath.to_s
|
25
17
|
@type = type.to_s
|
26
18
|
end
|
27
19
|
|
@@ -47,6 +39,9 @@ module Xcodeproj
|
|
47
39
|
#
|
48
40
|
def self.from_node(xml_node)
|
49
41
|
type, path = xml_node.attribute('location').value.split(':', 2)
|
42
|
+
if type == 'group'
|
43
|
+
path = prepend_parent_path(xml_node, path)
|
44
|
+
end
|
50
45
|
new(path, type)
|
51
46
|
end
|
52
47
|
|
@@ -1,41 +1,39 @@
|
|
1
|
+
require 'xcodeproj/workspace/reference'
|
2
|
+
|
1
3
|
module Xcodeproj
|
2
4
|
class Workspace
|
3
5
|
# Describes a group reference of a Workspace.
|
4
6
|
#
|
5
|
-
class GroupReference
|
7
|
+
class GroupReference < Reference
|
6
8
|
# @return [String] the name of the group
|
7
9
|
#
|
8
10
|
attr_reader :name
|
9
11
|
|
10
|
-
# @return [String] the
|
11
|
-
#
|
12
|
-
# This can be of the following values:
|
13
|
-
# - absolute
|
14
|
-
# - group
|
15
|
-
# - container (only supported value)
|
16
|
-
# - developer
|
12
|
+
# @return [String] the location of the group on disk
|
17
13
|
#
|
18
|
-
attr_reader :
|
14
|
+
attr_reader :location
|
19
15
|
|
20
16
|
# @param [#to_s] name @see name
|
21
17
|
# @param [#to_s] type @see type
|
18
|
+
# @param [#to_s] location @see location
|
22
19
|
#
|
23
|
-
def initialize(name, type = 'container')
|
20
|
+
def initialize(name, type = 'container', location = '')
|
24
21
|
@name = name.to_s
|
25
22
|
@type = type.to_s
|
23
|
+
@location = location.to_s
|
26
24
|
end
|
27
25
|
|
28
26
|
# @return [Bool] Whether a group reference is equal to another.
|
29
27
|
#
|
30
28
|
def ==(other)
|
31
|
-
name == other.name && type == other.type
|
29
|
+
name == other.name && type == other.type && location == other.location
|
32
30
|
end
|
33
31
|
alias_method :eql?, :==
|
34
32
|
|
35
33
|
# @return [Fixnum] A hash identical for equals objects.
|
36
34
|
#
|
37
35
|
def hash
|
38
|
-
[name, type].hash
|
36
|
+
[name, type, location].hash
|
39
37
|
end
|
40
38
|
|
41
39
|
# Returns a group reference given XML representation.
|
@@ -46,16 +44,21 @@ module Xcodeproj
|
|
46
44
|
# @return [GroupReference] The new group reference instance.
|
47
45
|
#
|
48
46
|
def self.from_node(xml_node)
|
49
|
-
|
47
|
+
location_array = xml_node.attribute('location').value.split(':', 2)
|
48
|
+
type = location_array.first
|
49
|
+
location = location_array[1] || ''
|
50
|
+
if type == 'group'
|
51
|
+
location = prepend_parent_path(xml_node, location)
|
52
|
+
end
|
50
53
|
name = xml_node.attribute('name').value
|
51
|
-
new(name, type)
|
54
|
+
new(name, type, location)
|
52
55
|
end
|
53
56
|
|
54
57
|
# @return [REXML::Element] the XML representation of the group reference.
|
55
58
|
#
|
56
59
|
def to_node
|
57
60
|
REXML::Element.new('Group').tap do |element|
|
58
|
-
element.add_attribute('location', "#{type}
|
61
|
+
element.add_attribute('location', "#{type}:#{location}")
|
59
62
|
element.add_attribute('name', "#{name}")
|
60
63
|
end
|
61
64
|
end
|