xcodeproj 0.4.0.rc2 → 0.4.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/xcodeproj.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Xcodeproj
2
- VERSION = '0.4.0.rc2'
2
+ VERSION = '0.4.0.rc3'
3
3
 
4
4
  class PlainInformative < StandardError
5
5
  end
@@ -16,31 +16,37 @@ module Xcodeproj
16
16
  #
17
17
  KNOWN_ISAS = {
18
18
  'AbstractObject' => %w[
19
- PBXBuildFile
20
- AbstractBuildPhase
21
- PBXBuildRule
22
- XCBuildConfiguration
23
- XCConfigurationList
24
- PBXContainerItemProxy
25
- PBXFileReference
26
- PBXGroup
27
- PBXNativeTarget
28
- PBXProject
29
- PBXTargetDependency
19
+ PBXBuildFile
20
+ AbstractBuildPhase
21
+ PBXBuildRule
22
+ XCBuildConfiguration
23
+ XCConfigurationList
24
+ PBXContainerItemProxy
25
+ PBXFileReference
26
+ PBXGroup
27
+ PBXProject
28
+ PBXTargetDependency
29
+ PBXReferenceProxy
30
30
  ],
31
31
 
32
32
  'AbstractBuildPhase' => %w[
33
- PBXCopyFilesBuildPhase
34
- PBXResourcesBuildPhase
35
- PBXSourcesBuildPhase
36
- PBXFrameworksBuildPhase
37
- PBXHeadersBuildPhase
38
- PBXShellScriptBuildPhase
33
+ PBXCopyFilesBuildPhase
34
+ PBXResourcesBuildPhase
35
+ PBXSourcesBuildPhase
36
+ PBXFrameworksBuildPhase
37
+ PBXHeadersBuildPhase
38
+ PBXShellScriptBuildPhase
39
+ ],
40
+
41
+ 'AbstractTarget' => %w[
42
+ PBXNativeTarget
43
+ PBXAggregateTarget
44
+ PBXLegacyTarget
39
45
  ],
40
46
 
41
47
  'PBXGroup' => %w[
42
- XCVersionGroup
43
- PBXVariantGroup
48
+ XCVersionGroup
49
+ PBXVariantGroup
44
50
  ]
45
51
  }.freeze
46
52
 
@@ -176,6 +176,7 @@ module Xcodeproj
176
176
  #
177
177
  def new_from_plist(uuid, objects_by_uuid_plist, root_object = false)
178
178
  attributes = objects_by_uuid_plist[uuid]
179
+ raise "[Xcodeproj] Unable to find attributes for UUID #{uuid}" unless attributes
179
180
  klass = Object.const_get(attributes['isa'])
180
181
  object = klass.new(self, uuid)
181
182
  object.add_referrer(self) if root_object
@@ -156,6 +156,11 @@ module Xcodeproj
156
156
  list = attrb.get_value(self)
157
157
  list.delete(object)
158
158
  end
159
+
160
+ references_by_keys_attributes.each do |attrb|
161
+ list = attrb.get_value(self)
162
+ list.each { |dictionary| dictionary.remove_reference(object) }
163
+ end
159
164
  end
160
165
 
161
166
  # @!group Plist related methods
@@ -191,12 +196,25 @@ module Xcodeproj
191
196
  to_many_attributes.each do |attrb|
192
197
  ref_uuids = object_plist[attrb.plist_name] || []
193
198
  list = attrb.get_value(self)
194
- ref_uuids.each do |ref_uuid|
195
- ref = project.objects_by_uuid[ref_uuid] || project.new_from_plist(ref_uuid, objects_by_uuid_plist)
196
- list << ref
199
+ ref_uuids.each do |uuid|
200
+ list << object_with_uuid(uuid, objects_by_uuid_plist)
201
+ end
202
+ object_plist.delete(attrb.plist_name)
203
+ end
204
+
205
+ references_by_keys_attributes.each do |attrb|
206
+ hashes = object_plist[attrb.plist_name] || {}
207
+ list = attrb.get_value(self)
208
+ hashes.each do |hash|
209
+ dictionary = ObjectDictionary.new(attrb, self)
210
+ hash.each do |key, uuid|
211
+ dictionary[key] = object_with_uuid(uuid, objects_by_uuid_plist)
212
+ end
213
+ list << dictionary
197
214
  end
198
215
  object_plist.delete(attrb.plist_name)
199
216
  end
217
+
200
218
  unless object_plist.empty?
201
219
  raise "[!] Xcodeproj doesn't know about the following attributes " \
202
220
  "#{object_plist} for the '#{isa}' isa.\n" \
@@ -204,6 +222,15 @@ module Xcodeproj
204
222
  end
205
223
  end
206
224
 
225
+ def object_with_uuid(uuid, objects_by_uuid_plist)
226
+ project.objects_by_uuid[uuid] || project.new_from_plist(uuid, objects_by_uuid_plist)
227
+ rescue NameError => exception
228
+ attributes = objects_by_uuid_plist[uuid]
229
+ raise "`#{isa}` attempted to initialize an object with unknown ISA "\
230
+ "`#{attributes['isa']}` from attributes: `#{attributes}`\n" \
231
+ "Please file and issue: https://github.com/CocoaPods/Xcodeproj/issues/new"
232
+ end
233
+
207
234
  # @note the key for simple and to_one attributes usually appears only
208
235
  # if there is a value. To many keys always appear with an empty
209
236
  # array.
@@ -227,6 +254,11 @@ module Xcodeproj
227
254
  plist[attrb.plist_name] = list.uuids
228
255
  end
229
256
 
257
+ references_by_keys_attributes.each do |attrb|
258
+ list = attrb.get_value(self)
259
+ plist[attrb.plist_name] = list.map { |dictionary| dictionary.to_plist }
260
+ end
261
+
230
262
  plist
231
263
  end
232
264
  alias :to_hash :to_plist
@@ -262,6 +294,11 @@ module Xcodeproj
262
294
  hash[attrb.plist_name] = list.map { |obj| obj.to_tree_hash }
263
295
  end
264
296
 
297
+ references_by_keys_attributes.each do |attrb|
298
+ list = attrb.get_value(self)
299
+ hash[attrb.plist_name] = list.map { |dictionary| dictionary.to_tree_hash }
300
+ end
301
+
265
302
  hash
266
303
  end
267
304
 
@@ -285,8 +322,9 @@ module Xcodeproj
285
322
  end
286
323
  end
287
324
 
288
- require 'xcodeproj/project/object_attributes'
289
325
  require 'xcodeproj/project/object_list'
326
+ require 'xcodeproj/project/object_dictionary'
327
+ require 'xcodeproj/project/object_attributes'
290
328
 
291
329
  # Required because some classes have cyclical references to each other.
292
330
  #
@@ -317,4 +355,5 @@ require 'xcodeproj/project/object/group'
317
355
  require 'xcodeproj/project/object/native_target'
318
356
  require 'xcodeproj/project/object/root_object'
319
357
  require 'xcodeproj/project/object/target_dependency'
358
+ require 'xcodeproj/project/object/reference_proxy'
320
359
 
File without changes
@@ -121,6 +121,12 @@ module Xcodeproj
121
121
 
122
122
  end
123
123
 
124
+ # Apparently a build phase named `Build Carbon Resources` (Observed for
125
+ # kernel extensions targets).
126
+ #
127
+ class PBXRezBuildPhase < AbstractBuildPhase
128
+ end
129
+
124
130
  class AbstractBuildPhase < AbstractObject
125
131
 
126
132
  ## CONVENIENCE METHODS #################################################
@@ -148,13 +154,42 @@ module Xcodeproj
148
154
  files << build_file
149
155
  build_file
150
156
  end
151
-
157
+
158
+ # Removes the build file associated with the given file reference from
159
+ # the phase.
160
+ #
161
+ # @param [PBXFileReference] file the file to remove
162
+ #
163
+ # @return [void]
164
+ #
152
165
  def remove_file_reference(file)
153
166
  build_file = files.find { |bf| bf.file_ref == file }
154
- files.delete(build_file)
155
167
  build_file.file_ref = nil
156
168
  build_file.remove_from_project
157
169
  end
170
+
171
+ # Removes a build file from the phase and clears its relationship to
172
+ # the file reference.
173
+ #
174
+ # @param [PBXBuildFile] file the file to remove
175
+ #
176
+ # @return [void]
177
+ #
178
+ def remove_build_file(build_file)
179
+ build_file.file_ref = nil
180
+ build_file.remove_from_project
181
+ end
182
+
183
+ # Removes all the build files from the phase and clears their
184
+ # relationship to the file reference.
185
+ #
186
+ # @return [void]
187
+ #
188
+ def clear_build_files
189
+ files.objects.each do |bf|
190
+ remove_build_file(bf)
191
+ end
192
+ end
158
193
  end
159
194
  end
160
195
  end
@@ -8,21 +8,32 @@ module Xcodeproj
8
8
  # This class is referenced by {PBXTargetDependency}; for information
9
9
  # about it usage see the specs of that class.
10
10
  #
11
- # @note This class references the other objects by uuid instead of
11
+ # @note This class references the other objects by UUID instead of
12
12
  # creating proper relationships because the other objects might be
13
13
  # part of another project. This implies that the references to
14
- # other objects should not increase the retain coutn of the
14
+ # other objects should not increase the retain count of the
15
15
  # targets.
16
16
  #
17
- # @todo: this class needs some work to support targets accross workspaces,
17
+ # @todo: this class needs some work to support targets across workspaces,
18
18
  # as the container portal might not be initialized leading
19
- # xcodproj to raise because ti can't find the UUID.
19
+ # xcodeproj to raise because ti can't find the UUID.
20
20
  #
21
21
  class PBXContainerItemProxy < AbstractObject
22
22
 
23
23
  # @return [String] apparently the UUID of the root object
24
24
  # {PBXProject} of the project containing the represented object.
25
25
  #
26
+ # @todo this is an attribute because a it is usually a reference to the
27
+ # root object or to a file reference to another project. The
28
+ # reference to the root object causes a retain cycle that could
29
+ # cause issues (e.g. in to_tree_hash). Usually those objects are
30
+ # retained by at least another object (the {Project} for the root
31
+ # object and a {PBXGroup} for the reference to another project)
32
+ # and so the referenced object should be serialized.
33
+ #
34
+ # If this assumption is incorrect, there could be loss of
35
+ # information opening and saving an existing project.
36
+ #
26
37
  attribute :container_portal, String
27
38
 
28
39
  # @return [String] the type of the proxy.
@@ -34,7 +45,15 @@ module Xcodeproj
34
45
  # @return [String] apparently the UUID of the represented
35
46
  # object.
36
47
  #
37
- # @note If the object is in another project this will return nil.
48
+ # @note If the object is in another project the UUID would not be
49
+ # present in the {Project#objects_by_uuid} hash. For this reason
50
+ # this is not an `has_one` attribute. It is assumes that if the
51
+ # object belongs to the project at least another object should be
52
+ # retaining it. This assumption is reasonable because this is a
53
+ # proxy class.
54
+ #
55
+ # If this assumption is incorrect, there could be loss of
56
+ # information opening and saving an existing project.
38
57
  #
39
58
  attribute :remote_global_id_string, String
40
59
 
@@ -10,7 +10,7 @@ module Xcodeproj
10
10
  # @return [ObjectList<PBXGroup, PBXFileReference>]
11
11
  # the objects contained by the group.
12
12
  #
13
- has_many :children, [PBXGroup, PBXFileReference]
13
+ has_many :children, [PBXGroup, PBXFileReference, PBXReferenceProxy]
14
14
 
15
15
  # @return [String] the source tree to which this group is relative.
16
16
  #
@@ -57,7 +57,7 @@ module Xcodeproj
57
57
 
58
58
  end
59
59
 
60
- # The purpose of this subclass is not understood.
60
+ # This class is used to gather localized files into one entry.
61
61
  #
62
62
  class PBXVariantGroup < PBXGroup
63
63
 
@@ -209,6 +209,16 @@ module Xcodeproj
209
209
  find_subpath(path)
210
210
  end
211
211
 
212
+ # Removes children files and groups under this group.
213
+ #
214
+ def remove_children_recursively
215
+ groups.each do |g|
216
+ g.remove_children_recursively
217
+ g.remove_from_project
218
+ end
219
+ files.each { |f| f.remove_from_project }
220
+ end
221
+
212
222
  # Traverses the children groups and finds the children with the given
213
223
  # path, optionally, creating any needed group. If the given path is
214
224
  # `nil` it returns itself.
@@ -2,20 +2,48 @@ module Xcodeproj
2
2
  class Project
3
3
  module Object
4
4
 
5
- # Represents a target.
6
- #
7
- class PBXNativeTarget < AbstractObject
5
+ class AbstractTarget < AbstractObject
8
6
 
9
7
  # @return [String] The name of the Target.
10
8
  #
11
9
  attribute :name, String
12
10
 
11
+ # @return [String] the name of the build product.
12
+ #
13
+ attribute :product_name, String
14
+
13
15
  # @return [XCConfigurationList] the list of the build configurations of
14
16
  # the target. This list commonly include two configurations `Debug`
15
17
  # and `Release`.
16
18
  #
17
19
  has_one :build_configuration_list, XCConfigurationList
18
20
 
21
+ # @return [PBXNativeTarget] the targets necessary to build this target.
22
+ #
23
+ has_many :dependencies, PBXTargetDependency
24
+
25
+ end
26
+
27
+ # Represents a target handled by Xcode.
28
+ #
29
+ class PBXNativeTarget < AbstractTarget
30
+
31
+ # @return [PBXBuildRule] the build rules of this target.
32
+ #
33
+ has_many :build_rules, PBXBuildRule
34
+
35
+ # @return [String] the build product type identifier.
36
+ #
37
+ attribute :product_type, String, 'com.apple.product-type.library.static'
38
+
39
+ # @return [PBXFileReference] the reference to the product file.
40
+ #
41
+ has_one :product_reference, PBXFileReference
42
+
43
+ # @return [String] the install path of the product.
44
+ #
45
+ attribute :product_install_path, String
46
+
19
47
  # @return [PBXBuildRule] the build phases of the target.
20
48
  #
21
49
  # @note Apparently only PBXCopyFilesBuildPhase and
@@ -23,31 +51,58 @@ module Xcodeproj
23
51
  #
24
52
  has_many :build_phases, AbstractBuildPhase
25
53
 
26
- # @return [PBXBuildRule] the build rules of this target.
54
+ end
55
+
56
+ # Represents a target that only consists in a aggregate of targets.
57
+ #
58
+ # @todo apparently it can't have build rules.
59
+ #
60
+ class PBXAggregateTarget < AbstractTarget
61
+
62
+ # @return [PBXBuildRule] the build phases of the target.
27
63
  #
28
- has_many :build_rules, PBXBuildRule
64
+ # @note Apparently only PBXCopyFilesBuildPhase and
65
+ # PBXShellScriptBuildPhase can appear multiple times in a target.
66
+ #
67
+ has_many :build_phases, [ PBXCopyFilesBuildPhase, PBXShellScriptBuildPhase ]
29
68
 
30
- # @return [PBXNativeTarget] the targets necessary to build this target.
69
+ end
70
+
71
+ # Represents a legacy target which uses an external build tool.
72
+ #
73
+ # Apparently it can't have any build phase but the attribute can be
74
+ # present.
75
+ #
76
+ class PBXLegacyTarget < AbstractTarget
77
+
78
+ # @return [String] e.g "Dir"
31
79
  #
32
- has_many :dependencies, PBXTargetDependency
80
+ attribute :build_working_directory, String
33
81
 
34
- # @return [String] the name of the build product.
82
+ # @return [String] e.g "$(ACTION)"
35
83
  #
36
- attribute :product_name, String
84
+ attribute :build_arguments_string, String
37
85
 
38
- # @return [String] the build product type identifier.
86
+ # @return [String] e.g "1"
39
87
  #
40
- attribute :product_type, String, 'com.apple.product-type.library.static'
88
+ attribute :pass_build_settings_in_environment, String
41
89
 
42
- # @return [PBXFileReference] the reference to the product file.
90
+ # @return [String] e.g "/usr/bin/make"
43
91
  #
44
- has_one :product_reference, PBXFileReference
92
+ attribute :build_tool_path, String
45
93
 
46
- # @return [String] the install path of the product.
94
+ # @return [PBXBuildRule] the build phases of the target.
47
95
  #
48
- attribute :product_install_path, String
96
+ # @note Apparently only PBXCopyFilesBuildPhase and
97
+ # PBXShellScriptBuildPhase can appear multiple times in a target.
98
+ #
99
+ has_many :build_phases, AbstractBuildPhase
100
+
101
+ end
102
+
103
+ #----------------------------------------------------------------------#
49
104
 
50
- ## CONVENIENCE METHODS #################################################
105
+ class AbstractTarget < AbstractObject
51
106
 
52
107
  # @!group Convenience methods
53
108
 
@@ -68,6 +123,54 @@ module Xcodeproj
68
123
  build_configuration_list.build_settings(build_configuration_name)
69
124
  end
70
125
 
126
+
127
+ # @return [Array<PBXCopyFilesBuildPhase>]
128
+ # the copy files build phases of the target.
129
+ #
130
+ def copy_files_build_phases
131
+ build_phases.select { |bp| bp.class == PBXCopyFilesBuildPhase }
132
+ end
133
+
134
+ # @return [Array<PBXShellScriptBuildPhase>]
135
+ # the copy files build phases of the target.
136
+ #
137
+ def shell_script_build_phases
138
+ build_phases.select { |bp| bp.class == PBXShellScriptBuildPhase }
139
+ end
140
+
141
+ # Creates a new copy files build phase.
142
+ #
143
+ # @param [String] name
144
+ # an optional name for the phase.
145
+ #
146
+ # @return [PBXCopyFilesBuildPhase] the new phase.
147
+ #
148
+ def new_copy_files_build_phase(name = nil)
149
+ phase = project.new(PBXCopyFilesBuildPhase)
150
+ phase.name = name
151
+ build_phases << phase
152
+ phase
153
+ end
154
+
155
+ # Creates a new shell script build phase.
156
+ #
157
+ # @param (see #new_copy_files_build_phase)
158
+ #
159
+ # @return [PBXShellScriptBuildPhase] the new phase.
160
+ #
161
+ def new_shell_script_build_phase(name = nil)
162
+ phase = project.new(PBXShellScriptBuildPhase)
163
+ phase.name = name
164
+ build_phases << phase
165
+ phase
166
+ end
167
+
168
+ end
169
+
170
+ class PBXNativeTarget < AbstractTarget
171
+
172
+ # @!group Convenience methods
173
+
71
174
  # Adds source files to the target.
72
175
  #
73
176
  # @param [Array<PBXFileReference>] file_references
@@ -88,8 +191,7 @@ module Xcodeproj
88
191
  header_extensions = Constants::HEADER_FILES_EXTENSIONS
89
192
  if (header_extensions.include?(extension))
90
193
  build_file.settings = { 'ATTRIBUTES' => ["Public"] }
91
- phase = headers_build_phase
92
- phase.files << build_file
194
+ headers_build_phase.files << build_file
93
195
  else
94
196
  build_file.settings = { 'COMPILER_FLAGS' => compiler_flags } if compiler_flags && !compiler_flags.empty?
95
197
  source_build_phase.files << build_file
@@ -97,9 +199,6 @@ module Xcodeproj
97
199
  end
98
200
  end
99
201
 
100
-
101
- # @!group Accessing build phases
102
-
103
202
  # Finds or creates the headers build phase of the target.
104
203
  #
105
204
  # @note A target should have only one headers build phase.
@@ -169,50 +268,6 @@ module Xcodeproj
169
268
  end
170
269
  bp
171
270
  end
172
-
173
- # @return [Array<PBXCopyFilesBuildPhase>]
174
- # the copy files build phases of the target.
175
- #
176
- def copy_files_build_phases
177
- build_phases.select { |bp| bp.class == PBXCopyFilesBuildPhase }
178
- end
179
-
180
- # @return [Array<PBXShellScriptBuildPhase>]
181
- # the copy files build phases of the target.
182
- #
183
- def shell_script_build_phases
184
- build_phases.select { |bp| bp.class == PBXShellScriptBuildPhase }
185
- end
186
-
187
-
188
- # @!group Creating build phases
189
-
190
- # Creates a new copy files build phase.
191
- #
192
- # @param [String] name
193
- # an optional name for the pahse.
194
- #
195
- # @return [PBXCopyFilesBuildPhase] the new phase.
196
- #
197
- def new_copy_files_build_phase(name = nil)
198
- phase = project.new(PBXCopyFilesBuildPhase)
199
- phase.name = name
200
- build_phases << phase
201
- phase
202
- end
203
-
204
- # Creates a new shell script build phase.
205
- #
206
- # @param (see #new_copy_files_build_phase)
207
- #
208
- # @return [PBXShellScriptBuildPhase] the new phase.
209
- #
210
- def new_shell_script_build_phase(name = nil)
211
- phase = project.new(PBXShellScriptBuildPhase)
212
- phase.name = name
213
- build_phases << phase
214
- phase
215
- end
216
271
  end
217
272
  end
218
273
  end
@@ -0,0 +1,35 @@
1
+ module Xcodeproj
2
+ class Project
3
+ module Object
4
+
5
+ # Apparently a proxy for a reference object which might belong another
6
+ # project contained in the same workspace of the project document.
7
+ #
8
+ # This class is used for referencing the products of another project.
9
+ #
10
+ class PBXReferenceProxy < AbstractObject
11
+
12
+ # @return [String] the path of the referenced filed.
13
+ #
14
+ attribute :path, String
15
+
16
+ # @return [String] the file type of the referenced filed.
17
+ #
18
+ attribute :file_type, String
19
+
20
+ # @return [PBXContainerItemProxy] the proxy to the project that
21
+ # contains the object.
22
+ #
23
+ has_one :remote_ref, PBXContainerItemProxy
24
+
25
+ # @return [String] the source tree for the path of the reference.
26
+ #
27
+ # E.g. "BUILT_PRODUCTS_DIR"
28
+ #
29
+ attribute :source_tree, String
30
+
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -9,7 +9,7 @@ module Xcodeproj
9
9
  # @return [ObjectList<PBXNativeTarget>] a list of all the targets in
10
10
  # the project.
11
11
  #
12
- has_many :targets, PBXNativeTarget
12
+ has_many :targets, AbstractTarget
13
13
 
14
14
  # @return [Hash{String => String}] attributes the attributes of the
15
15
  # target.
@@ -60,6 +60,13 @@ module Xcodeproj
60
60
  #
61
61
  attribute :project_root, String
62
62
 
63
+ # @return [Array<ObjectDictionary>] any reference to other projects.
64
+ #
65
+ has_many_references_by_keys :project_references, {
66
+ :project_ref => PBXFileReference,
67
+ :product_group => PBXGroup
68
+ }
69
+
63
70
  end
64
71
  end
65
72
  end
@@ -205,6 +205,10 @@ module Xcodeproj
205
205
  @to_many_attributes ||= attributes.select { |a| a.type == :to_many }
206
206
  end
207
207
 
208
+ def references_by_keys_attributes
209
+ @references_by_keys_attributes ||= attributes.select { |a| a.type == :references_by_keys }
210
+ end
211
+
208
212
  private
209
213
 
210
214
  # Defines a new simple attribute and synthesises the corresponding
@@ -330,6 +334,41 @@ module Xcodeproj
330
334
  end
331
335
  end
332
336
 
337
+ # Defines a new ordered relationship to many.
338
+ #
339
+ # @note This attribute only generates the reader method. Clients are
340
+ # not supposed to create {ObjectList} objects which are created
341
+ # by the methods synthesised by this attribute on demand.
342
+ # Clients, however can mutate the list according to its
343
+ # interface. The list is responsible to manage the reference
344
+ # counting for its values.
345
+ #
346
+ # @param [String] plural_name
347
+ # the name of the relationship.
348
+ #
349
+ # @param [Class, Array<Class>] isas
350
+ # the list of the classes corresponding to the accepted isas for
351
+ # this relationship.
352
+ #
353
+ # @macro [attach] has_many
354
+ # @!attribute [r] $1
355
+ #
356
+ def has_many_references_by_keys(plural_name, isas_hash)
357
+ attrb = AbstractObjectAttribute.new(:references_by_keys, plural_name, self)
358
+ attrb.classes = isas_hash.values
359
+ add_attribute(attrb)
360
+
361
+ define_method(attrb.name) do
362
+ # Here we are in the context of the instance
363
+ list = instance_variable_get("@#{attrb.name}")
364
+ unless list
365
+ list = ObjectList.new(attrb, self)
366
+ instance_variable_set("@#{attrb.name}", list)
367
+ end
368
+ list
369
+ end
370
+ end
371
+
333
372
  protected
334
373
 
335
374
  # Adds an attribute to the list of attributes of the class.
@@ -376,6 +415,12 @@ module Xcodeproj
376
415
  def to_many_attributes
377
416
  self.class.to_many_attributes
378
417
  end
418
+
419
+ # @return (see AbstractObject.to_many_attributes)
420
+ #
421
+ def references_by_keys_attributes
422
+ self.class.references_by_keys_attributes
423
+ end
379
424
  end # AbstractObject
380
425
  end
381
426
  end
@@ -0,0 +1,191 @@
1
+ module Xcodeproj
2
+ class Project
3
+
4
+ # This class represents relationships to other objects stored in a
5
+ # Dictionary.
6
+ #
7
+ # It works in conjunction with the {AbstractObject} class to ensure that
8
+ # the project is not serialized with unreachable objects by updating the
9
+ # with reference count on modifications.
10
+ #
11
+ # @note This class is a stub currently only being used by
12
+ # {PBXProject#project_references}. It doesn't perform type cheeking
13
+ # and the keys of the dictionary are in camel-case. To provide full
14
+ # support as the other classes the dictionary should be able to
15
+ #
16
+ # Give the following attribute:
17
+ #
18
+ # has_many_references_by_keys :project_references, {
19
+ # :project_ref => PBXFileReference,
20
+ # :product_group => PBXGroup
21
+ # }
22
+ #
23
+ # This should be possible:
24
+ #
25
+ # #=> Note the API:
26
+ # root_object.project_references.project_ref = file
27
+ #
28
+ # #=> This should raise:
29
+ # root_object.project_references.product_group = file
30
+ #
31
+ # generate setters and getters from the specification hash.
32
+ #
33
+ # Also the interface is a dirty hybrid between the
34
+ # {AbstractObjectAttribute} and the {ObjectList}.
35
+ #
36
+ # @note Concerning the mutations methods it is safe to call only those
37
+ # which are overridden to inform objects reference count. Ideally all
38
+ # the hash methods should be covered, but this is not done yet.
39
+ # Moreover it is a moving target because the methods of array
40
+ # usually are implemented in C
41
+ #
42
+ # @todo Cover all the mutations methods of the {Hash} class.
43
+ #
44
+ class ObjectDictionary < Hash
45
+
46
+ # {Xcodeproj} clients are not expected to create instances of
47
+ # {ObjectDictionary}, it is always initialized empty and automatically by
48
+ # the synthesized methods generated by {AbstractObject.has_many}.
49
+ #
50
+ def initialize(attribute, owner)
51
+ @attribute = attribute
52
+ @owner = owner
53
+ end
54
+
55
+ # @return [Array<Class>] The attribute that generated the list.
56
+ #
57
+ attr_reader :attribute
58
+
59
+ # @return [Array<Class>] The object that owns the list.
60
+ #
61
+ attr_reader :owner
62
+
63
+ #------------------------------------------------------------------------#
64
+
65
+ # @!group Notification enabled methods
66
+
67
+ # TODO: the overridden methods are incomplete.
68
+
69
+ # Associates an object to the given key and updates its references count.
70
+ #
71
+ # @param [String] key
72
+ # the key
73
+ #
74
+ # @param [AbstractObject] object
75
+ # the object to add to the dictionary.
76
+ #
77
+ # @return [void]
78
+ #
79
+ def []=(key, object)
80
+ if object
81
+ perform_additions_operations(object)
82
+ else
83
+ perform_deletion_operations(self[key])
84
+ end
85
+ super
86
+ end
87
+
88
+ # Removes the given key from the dictionary and informs the object that
89
+ # is not longer referenced by the owner.
90
+ #
91
+ # @param [String] key
92
+ # the key
93
+ #
94
+ # @return [void]
95
+ #
96
+ def delete(key)
97
+ object = self[key]
98
+ perform_deletion_operations(object)
99
+ super
100
+ end
101
+
102
+ #------------------------------------------------------------------------#
103
+
104
+ # @!group Integration with {AbstractObject}
105
+
106
+ # The plist reppresentation of the dictionary where the objects are
107
+ # replaced by their UUIDs.
108
+ #
109
+ # @return [Hash<String => String>]
110
+ #
111
+ def to_plist
112
+ result = {}
113
+ each { |key, obj| result[key] = obj.uuid }
114
+ result
115
+ end
116
+
117
+ # Returns a cascade reppresentation of the object without UUIDs.
118
+ #
119
+ # @return [Hash<String => String>]
120
+ #
121
+ def to_tree_hash
122
+ result = {}
123
+ each { |key, obj| result[key] = obj.to_tree_hash }
124
+ result
125
+ end
126
+
127
+ # Removes all the references to a given object.
128
+ #
129
+ # @return [void]
130
+ #
131
+ def remove_reference(object)
132
+ each { |key, obj| self[key] = nil if obj == object }
133
+ end
134
+
135
+ #------------------------------------------------------------------------#
136
+
137
+ # @!group Integration with {ObjectList}
138
+
139
+ # Informs the objects contained in the dictionary that another object is
140
+ # referencing them.
141
+ #
142
+ # @return [void]
143
+ #
144
+ def add_referrer(referrer)
145
+ values.each { |obj| obj.add_referrer(referrer) }
146
+ end
147
+
148
+ # Informs the objects contained in the dictionary that another object
149
+ # stopped referencing them.
150
+ #
151
+ # @return [void]
152
+ #
153
+ def remove_referrer(referrer)
154
+ values.each { |obj| obj.remove_referrer(referrer) }
155
+ end
156
+
157
+ #------------------------------------------------------------------------#
158
+
159
+ # @!group Notification Methods
160
+
161
+ private
162
+
163
+ # Informs an object that it was added to the dictionary. In practice it
164
+ # adds the owner of the list as referrer to the objects. It also
165
+ # validates the value.
166
+ #
167
+ # @return [void]
168
+ #
169
+ def perform_additions_operations(objects)
170
+ objects = [objects] unless objects.is_a?(Array)
171
+ objects.each do |obj|
172
+ obj.add_referrer(owner)
173
+ attribute.validate_value(obj)
174
+ end
175
+ end
176
+
177
+ # Informs an object that it was removed from to the dictionary, so it can
178
+ # remove it from its referrers and take the appropriate actions.
179
+ #
180
+ # @return [void]
181
+ #
182
+ def perform_deletion_operations(objects)
183
+ objects = [objects] unless objects.is_a?(Array)
184
+ objects.each do |obj|
185
+ obj.remove_referrer(owner)
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+
@@ -10,10 +10,10 @@ module Xcodeproj
10
10
  # @note Concerning the mutations methods it is safe to call only those
11
11
  # which are overridden to inform objects reference count. Ideally all
12
12
  # the array methods should be covered, but this is not done yet.
13
- # Moreover it is a moving target because the methods of array
13
+ # Moreover it is a moving target because the methods of array
14
14
  # usually are implemented in C
15
15
  #
16
- # @todo Cover all the array methods.
16
+ # @todo Cover all the mutations methods of the {Array} class.
17
17
  #
18
18
  class ObjectList < Array
19
19
 
@@ -26,40 +26,90 @@ module Xcodeproj
26
26
  @owner = owner
27
27
  end
28
28
 
29
- # @return [Array<Class>] The attribute that generated the list.
29
+ # @return [Array<Class>] the attribute that generated the list.
30
30
  #
31
31
  attr_reader :attribute
32
32
 
33
- # @return [Array<Class>] The object that owns the list.
33
+ # @return [Array<Class>] the object that owns the list.
34
34
  #
35
35
  attr_reader :owner
36
36
 
37
- # @return [Array<String>] The UUIDs of all the objects referenced by this
38
- # list.
37
+ #------------------------------------------------------------------------#
38
+
39
+ # @!group Integration with {ObjectList}
40
+
41
+ # @return [Array<String>]
42
+ # the UUIDs of all the objects referenced by this list.
39
43
  #
40
44
  def uuids
41
45
  map { |obj| obj.uuid }
42
46
  end
43
47
 
48
+ # @return [Array<AbstractObject>]
49
+ # a new array generated with the objects contained in the list.
50
+ #
51
+ def objects
52
+ to_a
53
+ end
54
+
55
+ #------------------------------------------------------------------------#
56
+
44
57
  # @!group Notification enabled methods
45
58
 
46
59
  # TODO: the overridden methods are incomplete.
47
60
 
61
+ # Adds an array of objects to list and updates their references count.
62
+ #
63
+ # @param [Array<AbstractObject, ObjectDictionary>] object
64
+ # an array of objects to add to the list.
65
+ #
66
+ # @return [void]
67
+ #
48
68
  def +(objects)
49
69
  super
50
70
  perform_additions_operations(objects)
51
71
  end
52
72
 
73
+ # Adds an object to list and updates its references count.
74
+ #
75
+ # @param [AbstractObject, ObjectDictionary] object
76
+ # the object to add to the list.
77
+ #
78
+ # @return [void]
79
+ #
53
80
  def <<(object)
54
81
  super
55
82
  perform_additions_operations(object)
56
83
  end
57
84
 
85
+ # Removes an object to list and updates its references count.
86
+ #
87
+ # @param [AbstractObject, ObjectDictionary] object
88
+ # the object to delete from the list.
89
+ #
90
+ # @return [void]
91
+ #
58
92
  def delete(object)
59
93
  super
60
94
  perform_deletion_operations(object)
61
95
  end
62
96
 
97
+ # Removes all the objects contained in the list and updates their
98
+ # reference counts.
99
+ #
100
+ # @return [void]
101
+ #
102
+ def clear
103
+ objects.each do |object|
104
+ perform_deletion_operations(object)
105
+ end
106
+ super
107
+ end
108
+
109
+ #------------------------------------------------------------------------#
110
+
111
+ # @!group Notification Methods
112
+
63
113
  private
64
114
 
65
115
  # Informs an object that it was added to the list. In practice it adds
@@ -72,19 +122,19 @@ module Xcodeproj
72
122
  objects = [objects] unless objects.is_a?(Array)
73
123
  objects.each do |obj|
74
124
  obj.add_referrer(owner)
75
- attribute.validate_value(obj)
125
+ attribute.validate_value(obj) unless obj.is_a?(ObjectDictionary)
76
126
  end
77
127
  end
78
128
 
79
129
  # Informs an object that it was removed from to the list, so it can
80
- # remove it from its referrers and take the appropriate actions.
130
+ # remove its owner from its referrers and take the appropriate actions.
81
131
  #
82
132
  # @return [void]
83
133
  #
84
134
  def perform_deletion_operations(objects)
85
135
  objects = [objects] unless objects.is_a?(Array)
86
136
  objects.each do |obj|
87
- obj.remove_referrer(owner)
137
+ obj.remove_referrer(owner) unless obj.is_a?(ObjectDictionary)
88
138
  end
89
139
  end
90
140
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcodeproj
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.rc2
4
+ version: 0.4.0.rc3
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-22 00:00:00.000000000 Z
12
+ date: 2012-10-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -60,6 +60,7 @@ files:
60
60
  - lib/xcodeproj/config.rb
61
61
  - lib/xcodeproj/constants.rb
62
62
  - lib/xcodeproj/helper.rb
63
+ - lib/xcodeproj/project/object/aggregate_target.rb
63
64
  - lib/xcodeproj/project/object/build_configuration.rb
64
65
  - lib/xcodeproj/project/object/build_file.rb
65
66
  - lib/xcodeproj/project/object/build_phase.rb
@@ -69,10 +70,12 @@ files:
69
70
  - lib/xcodeproj/project/object/file_reference.rb
70
71
  - lib/xcodeproj/project/object/group.rb
71
72
  - lib/xcodeproj/project/object/native_target.rb
73
+ - lib/xcodeproj/project/object/reference_proxy.rb
72
74
  - lib/xcodeproj/project/object/root_object.rb
73
75
  - lib/xcodeproj/project/object/target_dependency.rb
74
76
  - lib/xcodeproj/project/object.rb
75
77
  - lib/xcodeproj/project/object_attributes.rb
78
+ - lib/xcodeproj/project/object_dictionary.rb
76
79
  - lib/xcodeproj/project/object_list.rb
77
80
  - lib/xcodeproj/project/recursive_diff.rb
78
81
  - lib/xcodeproj/project.rb
@@ -99,7 +102,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
102
  version: '0'
100
103
  segments:
101
104
  - 0
102
- hash: 2919884453979069373
105
+ hash: 1005029667767319661
103
106
  required_rubygems_version: !ruby/object:Gem::Requirement
104
107
  none: false
105
108
  requirements: