xcoder 0.1.1 → 0.1.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.
- data/.gitignore +8 -0
- data/.rvmrc +4 -0
- data/Gemfile +14 -0
- data/Guardfile +14 -0
- data/README.md +142 -0
- data/Rakefile +28 -0
- data/lib/xcode/build_file.rb +22 -0
- data/lib/xcode/build_phase.rb +92 -0
- data/lib/xcode/builder.rb +190 -0
- data/lib/xcode/buildfile.rb +101 -0
- data/lib/xcode/configuration.rb +161 -0
- data/lib/xcode/configuration_list.rb +85 -0
- data/lib/xcode/core_ext/array.rb +23 -0
- data/lib/xcode/core_ext/boolean.rb +21 -0
- data/lib/xcode/core_ext/fixnum.rb +5 -0
- data/lib/xcode/core_ext/hash.rb +27 -0
- data/lib/xcode/core_ext/string.rb +26 -0
- data/lib/xcode/file_reference.rb +77 -0
- data/lib/xcode/group.rb +197 -0
- data/lib/xcode/info_plist.rb +41 -0
- data/lib/xcode/keychain.rb +122 -0
- data/lib/xcode/parsers/plutil_project_parser.rb +20 -0
- data/lib/xcode/project.rb +335 -0
- data/lib/xcode/provisioning_profile.rb +53 -0
- data/lib/xcode/registry.rb +168 -0
- data/lib/xcode/resource.rb +203 -0
- data/lib/xcode/scheme.rb +36 -0
- data/lib/xcode/shell.rb +22 -0
- data/lib/xcode/simple_identifier_generator.rb +17 -0
- data/lib/xcode/target.rb +204 -0
- data/lib/xcode/test/formatters/junit_formatter.rb +50 -0
- data/lib/xcode/test/ocunit_report_parser.rb +68 -0
- data/lib/xcode/test/suite_result.rb +31 -0
- data/lib/xcode/test/test_result.rb +38 -0
- data/lib/xcode/testflight.rb +56 -0
- data/lib/xcode/variant_group.rb +28 -0
- data/lib/xcode/version.rb +3 -0
- data/lib/xcode/workspace.rb +40 -0
- data/lib/xcoder.rb +105 -0
- data/xcoder.gemspec +26 -0
- metadata +52 -12
@@ -0,0 +1,335 @@
|
|
1
|
+
require 'plist'
|
2
|
+
require 'xcode/parsers/plutil_project_parser'
|
3
|
+
require 'xcode/resource'
|
4
|
+
require 'xcode/target'
|
5
|
+
require 'xcode/configuration'
|
6
|
+
require 'xcode/scheme'
|
7
|
+
require 'xcode/group'
|
8
|
+
require 'xcode/file_reference'
|
9
|
+
require 'xcode/registry'
|
10
|
+
require 'xcode/build_phase'
|
11
|
+
require 'xcode/variant_group'
|
12
|
+
require 'xcode/configuration_list'
|
13
|
+
|
14
|
+
module Xcode
|
15
|
+
class Project
|
16
|
+
|
17
|
+
attr_reader :name, :sdk, :path, :schemes, :registry
|
18
|
+
|
19
|
+
#
|
20
|
+
# Initialized with a specific path and sdk.
|
21
|
+
#
|
22
|
+
# This initialization is not often used. Instead projects are generated
|
23
|
+
# through the Xcode#project method.
|
24
|
+
#
|
25
|
+
# @see Xcode
|
26
|
+
#
|
27
|
+
# @param [String] path of the project to open.
|
28
|
+
# @param [String] sdk the sdk value of the project. This will default to
|
29
|
+
# `iphoneos`.
|
30
|
+
#
|
31
|
+
def initialize(path, sdk=nil)
|
32
|
+
@sdk = sdk || "iphoneos" # FIXME: should support OSX/simulator too
|
33
|
+
@path = File.expand_path path
|
34
|
+
@schemes = []
|
35
|
+
@groups = []
|
36
|
+
@name = File.basename(@path).gsub(/\.xcodeproj/,'')
|
37
|
+
|
38
|
+
# Parse the Xcode project file and create the registry
|
39
|
+
|
40
|
+
@registry = parse_pbxproj
|
41
|
+
@project = Xcode::Resource.new registry.root, @registry
|
42
|
+
|
43
|
+
@schemes = parse_schemes
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Returns the main group of the project where all the files reside.
|
48
|
+
#
|
49
|
+
# @todo this really could use a better name then groups as it is the mainGroup
|
50
|
+
# but it should likely be something like main_group, root or something
|
51
|
+
# else that conveys that this is the project root for files, and such.
|
52
|
+
#
|
53
|
+
# @return [Group] the main group, the heart of the action of the file
|
54
|
+
# explorer for the Xcode project. From here all other groups and items
|
55
|
+
# may be found.
|
56
|
+
#
|
57
|
+
def groups
|
58
|
+
@project.main_group
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Returns the group specified. If any part of the group does not exist along
|
63
|
+
# the path the group is created. Also paths can be specified to make the
|
64
|
+
# traversing of the groups easier.
|
65
|
+
#
|
66
|
+
# @example a group path that contains a traversal to sub-groups
|
67
|
+
#
|
68
|
+
# project.group('Vendor/MyCode/Support Files')
|
69
|
+
# # is equivalent to...
|
70
|
+
# project.group('Vendor').first.group('MyCode').first.group('Supporting Files')
|
71
|
+
#
|
72
|
+
# @note this path functionality current is only exercised from the project level
|
73
|
+
# all groups will treat the path division `/` as simply a character.
|
74
|
+
#
|
75
|
+
# @note this will attempt to find the paths specified, if it fails to find them
|
76
|
+
# it will create one and then continue traversing.
|
77
|
+
#
|
78
|
+
# @param [String] name the group name to find/create
|
79
|
+
#
|
80
|
+
def group(name,&block)
|
81
|
+
|
82
|
+
current_group = @project.main_group
|
83
|
+
|
84
|
+
# @todo consider this traversing and find/create as a normal procedure when
|
85
|
+
# traversing the project.
|
86
|
+
|
87
|
+
name.split("/").each do |path_component|
|
88
|
+
found_group = current_group.group(path_component).first
|
89
|
+
found_group = current_group.create_group(path_component) unless found_group
|
90
|
+
current_group = found_group
|
91
|
+
end
|
92
|
+
|
93
|
+
current_group.instance_eval(&block) if block_given?
|
94
|
+
|
95
|
+
current_group
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Return the file that matches the specified path. This will traverse
|
100
|
+
# the project's groups and find the file at the end of the path.
|
101
|
+
#
|
102
|
+
# @param [String] name_with_path the path to the file
|
103
|
+
# @return [FileReference] the file that matches the name, nil if no file
|
104
|
+
# matches the path.
|
105
|
+
def file(name_with_path)
|
106
|
+
path, name = File.split(name_with_path)
|
107
|
+
group(path).file(name).first
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Most Xcode projects have a products group where products are placed. This
|
112
|
+
# will generate an exception if there is no products group.
|
113
|
+
#
|
114
|
+
# @return [Group] the 'Products' group of the project.
|
115
|
+
def products_group
|
116
|
+
groups.group('Products').first
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Most Xcode projects have a Frameworks gorup where all the imported
|
121
|
+
# frameworks are shown. This will generate an exception if there is no
|
122
|
+
# Frameworks group.
|
123
|
+
#
|
124
|
+
# @return [Group] the 'Frameworks' group of the projet.
|
125
|
+
def frameworks_group
|
126
|
+
groups.group('Frameworks').first
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# This will convert the current project file into a supported Xcode Plist
|
131
|
+
# format. This format is not json or a traditional plist so several core
|
132
|
+
# Ruby objects gained the #to_xcplist method to save it properly.
|
133
|
+
#
|
134
|
+
# Specifically this will add the necessary file header information and the
|
135
|
+
# surrounding mustache braces around the xcode plist format of the registry.
|
136
|
+
#
|
137
|
+
# @return [String] Xcode Plist format of the project.
|
138
|
+
def to_xcplist
|
139
|
+
|
140
|
+
# @note The Hash#to_xcplist, which the Registry will save out as xcode,
|
141
|
+
# saves a semi-colon at the end which needs to be removed to ensure
|
142
|
+
# the project file can be opened.
|
143
|
+
|
144
|
+
%{// !$*UTF8*$!"\n#{@registry.to_xcplist.gsub(/\};\s*\z/,'}')}}
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# Save the current project at the current path that it exists.
|
149
|
+
#
|
150
|
+
def save!
|
151
|
+
save @path
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
# Saves the current project at the specified path.
|
156
|
+
#
|
157
|
+
# @note currently this does not support saving the workspaces associated
|
158
|
+
# with the project to their new location.
|
159
|
+
#
|
160
|
+
# @param [String] path the path to save the project
|
161
|
+
#
|
162
|
+
def save(path)
|
163
|
+
Dir.mkdir(path) unless File.exists?(path)
|
164
|
+
|
165
|
+
project_filepath = "#{path}/project.pbxproj"
|
166
|
+
|
167
|
+
# @toodo Save the workspace when the project is saved
|
168
|
+
# FileUtils.cp_r "#{path}/project.xcworkspace", "#{path}/project.xcworkspace"
|
169
|
+
|
170
|
+
File.open(project_filepath,'w') do |file|
|
171
|
+
file.puts to_xcplist
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Return the scheme with the specified name. Raises an error if no schemes
|
177
|
+
# match the specified name.
|
178
|
+
#
|
179
|
+
# @note if two schemes match names, the first matching scheme is returned.
|
180
|
+
#
|
181
|
+
# @param [String] name of the specific scheme
|
182
|
+
# @return [Scheme] the specific scheme that matches the name specified
|
183
|
+
#
|
184
|
+
def scheme(name)
|
185
|
+
scheme = @schemes.select {|t| t.name == name.to_s}.first
|
186
|
+
raise "No such scheme #{name}, available schemes are #{@schemes.map {|t| t.name}.join(', ')}" if scheme.nil?
|
187
|
+
yield scheme if block_given?
|
188
|
+
scheme
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# All the targets specified within the project.
|
193
|
+
#
|
194
|
+
# @return [Array<PBXNativeTarget>] an array of all the available targets for
|
195
|
+
# the specific project.
|
196
|
+
#
|
197
|
+
def targets
|
198
|
+
@project.targets.map do |target|
|
199
|
+
target.project = self
|
200
|
+
target
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
#
|
205
|
+
# Return the target with the specified name. Raises an error if no targets
|
206
|
+
# match the specified name.
|
207
|
+
#
|
208
|
+
# @note if two targets match names, the first matching target is returned.
|
209
|
+
#
|
210
|
+
# @param [String] name of the specific target
|
211
|
+
# @return [PBXNativeTarget] the specific target that matches the name specified
|
212
|
+
#
|
213
|
+
def target(name)
|
214
|
+
target = targets.select {|t| t.name == name.to_s}.first
|
215
|
+
raise "No such target #{name}, available targets are #{targets.map {|t| t.name}.join(', ')}" if target.nil?
|
216
|
+
yield target if block_given?
|
217
|
+
target
|
218
|
+
end
|
219
|
+
|
220
|
+
#
|
221
|
+
# Creates a new target within the Xcode project. This will by default not
|
222
|
+
# generate all the additional build phases, configurations, and files
|
223
|
+
# that create a project.
|
224
|
+
#
|
225
|
+
# @todo generate a create target with sensible defaults, similar to how
|
226
|
+
# it is done through Xcode itself.
|
227
|
+
#
|
228
|
+
# @todo based on the specified type of target, default build phases and
|
229
|
+
# configuration should be created for the target similar to what is
|
230
|
+
# supported in xcode. Currently even now the :ios target does not
|
231
|
+
# generate the deafult build_phases for you and requires you to make those.
|
232
|
+
#
|
233
|
+
# @param [String] name the name to provide to the target. This will also
|
234
|
+
# be the value that other defaults will be based on.
|
235
|
+
#
|
236
|
+
def create_target(name,type=:ios)
|
237
|
+
|
238
|
+
target = @registry.add_object Target.send(type)
|
239
|
+
@project.properties['targets'] << target.identifier
|
240
|
+
|
241
|
+
target.name = name
|
242
|
+
|
243
|
+
build_configuration_list = @registry.add_object(ConfigurationList.configration_list)
|
244
|
+
target.build_configuration_list = build_configuration_list.identifier
|
245
|
+
|
246
|
+
target.project = self
|
247
|
+
|
248
|
+
yield target if block_given?
|
249
|
+
|
250
|
+
target.save!
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
# Remove a target from the Xcode project.
|
255
|
+
#
|
256
|
+
# @note this will remove the first target that matches the specified name.
|
257
|
+
#
|
258
|
+
# @note this will remove only the project entry at the moment and not the
|
259
|
+
# the files that may be associated with the target. All build phases,
|
260
|
+
# build files, and configurations will automatically be cleaned up when
|
261
|
+
# Xcode is opened.
|
262
|
+
#
|
263
|
+
# @param [String] name the name of the target to remove from the Xcode
|
264
|
+
# project.
|
265
|
+
#
|
266
|
+
def remove_target(name)
|
267
|
+
found_target = targets.find {|target| target.name == name }
|
268
|
+
if found_target
|
269
|
+
@project.properties['targets'].delete found_target.identifier
|
270
|
+
@registry.remove_object found_target.identifier
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
|
276
|
+
#
|
277
|
+
# Prints to STDOUT a description of this project's targets, configuration and schemes.
|
278
|
+
#
|
279
|
+
def describe
|
280
|
+
puts "Project #{name} contains"
|
281
|
+
targets.each do |t|
|
282
|
+
puts " + target:#{t.name}"
|
283
|
+
t.configs.each do |c|
|
284
|
+
puts " + config:#{c.name}"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
schemes.each do |s|
|
288
|
+
puts " + scheme #{s.name}"
|
289
|
+
puts " + Launch action => target:#{s.launch.target.name}, config:#{s.launch.name}" unless s.launch.nil?
|
290
|
+
puts " + Test action => target:#{s.test.target.name}, config:#{s.test.name}" unless s.test.nil?
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
private
|
295
|
+
|
296
|
+
#
|
297
|
+
# Parse all the scheme files that can be found within the project. Schemes
|
298
|
+
# can be defined as `shared` schemes and then `user` specific schemes. Parsing
|
299
|
+
# the schemes will load the shared ones and then the current acting user's
|
300
|
+
# schemes.
|
301
|
+
#
|
302
|
+
def parse_schemes
|
303
|
+
shared_schemes = Dir["#{@path}/xcshareddata/xcschemes/*.xcscheme"]
|
304
|
+
user_specific_schemes = Dir["#{@path}/xcuserdata/#{ENV['USER']}.xcuserdatad/xcschemes/*.xcscheme"]
|
305
|
+
|
306
|
+
(shared_schemes + user_specific_schemes).map do |scheme|
|
307
|
+
Xcode::Scheme.new(self, scheme)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
#
|
312
|
+
# Using the sytem tool plutil, the specified project file is parsed and
|
313
|
+
# converted to JSON, which is then converted to a hash object.
|
314
|
+
#
|
315
|
+
# This content contains all the data within the project file and is used
|
316
|
+
# to create the Registry.
|
317
|
+
#
|
318
|
+
# @return [Resource] a resource mapped to the root resource within the project
|
319
|
+
# this is generally the project file which contains details about the main
|
320
|
+
# group, targets, etc.
|
321
|
+
#
|
322
|
+
# @see Registry
|
323
|
+
#
|
324
|
+
def parse_pbxproj
|
325
|
+
registry = Xcode::PLUTILProjectParser.parse "#{@path}/project.pbxproj"
|
326
|
+
|
327
|
+
class << registry
|
328
|
+
include Xcode::Registry
|
329
|
+
end
|
330
|
+
|
331
|
+
registry
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Xcode
|
2
|
+
class ProvisioningProfile
|
3
|
+
attr_reader :path, :name, :uuid, :identifiers
|
4
|
+
def initialize(path)
|
5
|
+
|
6
|
+
raise "Provisioning profile '#{path}' does not exist" unless File.exists? path
|
7
|
+
|
8
|
+
@path = path
|
9
|
+
@identifiers = []
|
10
|
+
|
11
|
+
# TODO: im sure this could be done in a nicer way. maybe read out the XML-like stuff and use the plist -> json converter
|
12
|
+
uuid = nil
|
13
|
+
File.open(path, "rb") do |f|
|
14
|
+
input = f.read
|
15
|
+
input=~/<key>UUID<\/key>.*?<string>(.*?)<\/string>/im
|
16
|
+
@uuid = $1.strip
|
17
|
+
|
18
|
+
input=~/<key>Name<\/key>.*?<string>(.*?)<\/string>/im
|
19
|
+
@name = $1.strip
|
20
|
+
|
21
|
+
input=~/<key>ApplicationIdentifierPrefix<\/key>.*?<array>(.*?)<\/array>/im
|
22
|
+
$1.split(/<string>/).each do |id|
|
23
|
+
next if id.nil? or id.strip==""
|
24
|
+
@identifiers << id.gsub(/<\/string>/,'').strip
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.profiles_path
|
31
|
+
File.expand_path "~/Library/MobileDevice/Provisioning\\ Profiles/"
|
32
|
+
end
|
33
|
+
|
34
|
+
def install_path
|
35
|
+
"#{ProvisioningProfile.profiles_path}/#{self.uuid}.mobileprovision"
|
36
|
+
end
|
37
|
+
|
38
|
+
def install
|
39
|
+
Xcode::Shell.execute("cp #{self.path} #{self.install_path}")
|
40
|
+
end
|
41
|
+
|
42
|
+
def uninstall
|
43
|
+
Xcode::Shell.execute("rm -f #{self.install_path}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.installed_profiles
|
47
|
+
Dir["#{self.profiles_path}/*.mobileprovision"].map do |file|
|
48
|
+
ProvisioningProfile.new(file)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'xcode/simple_identifier_generator'
|
2
|
+
|
3
|
+
module Xcode
|
4
|
+
|
5
|
+
#
|
6
|
+
# The Registry represents the parsed data from the Xcode Project file. Namely
|
7
|
+
# the registry is a Hash that provides additional functionality to allow the
|
8
|
+
# the ability to query, add, and remove resources from the object hash.
|
9
|
+
#
|
10
|
+
# Opening the Xcode project file in a text-editor you'll notice that it is a
|
11
|
+
# big hash/plist with a file core keys. The most important key is the 'objects'
|
12
|
+
# dictionary which maintains the master-list of Identifiers to properties. All
|
13
|
+
# objects are represented here and all other resources use the reference
|
14
|
+
# to make the connection to the objects.
|
15
|
+
#
|
16
|
+
# @see Project
|
17
|
+
#
|
18
|
+
module Registry
|
19
|
+
|
20
|
+
#
|
21
|
+
# This method is used internally to determine if the value that is being
|
22
|
+
# retrieved is an identifier.
|
23
|
+
#
|
24
|
+
# @param [String] value is the specified value in the form of an identifier
|
25
|
+
#
|
26
|
+
def self.is_identifier? value
|
27
|
+
value =~ /^[0-9A-F]{24}$/
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Objects within the registry contain an `isa` property, which translates
|
32
|
+
# to modules which can be mixed in to provide additional functionality.
|
33
|
+
#
|
34
|
+
# @param [String] isa the type of the object.
|
35
|
+
#
|
36
|
+
def self.isa_to_module isa
|
37
|
+
|
38
|
+
{ 'XCBuildConfiguration' => Configuration,
|
39
|
+
'PBXFileReference' => FileReference,
|
40
|
+
'PBXGroup' => Group,
|
41
|
+
'PBXNativeTarget' => Target,
|
42
|
+
'PBXAggregateTarget' => Target,
|
43
|
+
'PBXFrameworksBuildPhase' => BuildPhase,
|
44
|
+
'PBXSourcesBuildPhase' => BuildPhase,
|
45
|
+
'PBXResourcesBuildPhase' => BuildPhase,
|
46
|
+
'PBXBuildFile' => BuildFile,
|
47
|
+
'PBXVariantGroup' => VariantGroup,
|
48
|
+
'XCConfigurationList' => ConfigurationList,
|
49
|
+
'PBXVariantGroup' => VariantGroup }[isa]
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# This is the root object of the project. This is generally an identifier
|
54
|
+
# pointing to a project.
|
55
|
+
#
|
56
|
+
def root
|
57
|
+
self['rootObject']
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
#
|
62
|
+
# This is a hash of all the objects within the project. The keys are the
|
63
|
+
# unique identifiers which are 24 length hexadecimal strings. The values
|
64
|
+
# are the objects that the keys represent.
|
65
|
+
#
|
66
|
+
# @return [Hash] that contains all the objects in the project.
|
67
|
+
#
|
68
|
+
def objects
|
69
|
+
self['objects']
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Retrieve a Resource for the given identifier.
|
74
|
+
#
|
75
|
+
# @param [String] identifier the unique identifier for the resource you are
|
76
|
+
# attempting to find.
|
77
|
+
# @return [Resource] the Resource object the the data properties that would
|
78
|
+
# be stored wihin it.
|
79
|
+
#
|
80
|
+
def object(identifier)
|
81
|
+
Resource.new identifier, self
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Retrieve the properties Hash for the given identifer.
|
86
|
+
#
|
87
|
+
# @param [String] identifier the unique identifier for the resource you
|
88
|
+
# are attempting to find.
|
89
|
+
#
|
90
|
+
# @return [Hash] the raw, properties hash for the particular resource; nil
|
91
|
+
# if nothing matches the identifier.
|
92
|
+
#
|
93
|
+
def properties(identifier)
|
94
|
+
objects[identifier]
|
95
|
+
end
|
96
|
+
|
97
|
+
MAX_IDENTIFIER_GENERATION_ATTEMPTS = 10
|
98
|
+
|
99
|
+
#
|
100
|
+
# Provides a method to generically add objects to the registry. This will
|
101
|
+
# create a unqiue identifier and add the specified parameters to the
|
102
|
+
# registry. As all objecst within a the project maintain a reference to this
|
103
|
+
# registry they can immediately query for newly created items.
|
104
|
+
#
|
105
|
+
# @note generally this method should not be called directly and instead
|
106
|
+
# resources should provide the ability to assist with generating the
|
107
|
+
# correct objects for the registry.
|
108
|
+
#
|
109
|
+
# @param [Hash] object_properties a hash that contains all the properties
|
110
|
+
# that are known for the particular item.
|
111
|
+
#
|
112
|
+
def add_object(object_properties)
|
113
|
+
|
114
|
+
new_identifier = SimpleIdentifierGenerator.generate
|
115
|
+
|
116
|
+
# Ensure that the identifier generated is unique
|
117
|
+
|
118
|
+
identifier_generation_count = 0
|
119
|
+
|
120
|
+
while objects.key?(new_identifier)
|
121
|
+
|
122
|
+
new_identifier = SimpleIdentifierGenerator.generate
|
123
|
+
|
124
|
+
# Increment our identifier generation count and if we reach our max raise
|
125
|
+
# an exception as something has gone horribly wrong.
|
126
|
+
|
127
|
+
identifier_generation_count += 1
|
128
|
+
if identifier_generation_count > MAX_IDENTIFIER_GENERATION_ATTEMPTS
|
129
|
+
raise "Unable to generate a unique identifier for object: #{object_properties}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
new_identifier = SimpleIdentifierGenerator.generate if objects.key?(new_identifier)
|
134
|
+
|
135
|
+
|
136
|
+
objects[new_identifier] = object_properties
|
137
|
+
|
138
|
+
Resource.new new_identifier, self
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Replace an existing object that shares that same identifier. This is how
|
143
|
+
# a Resource is saved back into the registry. So that it will be known to
|
144
|
+
# all other objects that it has changed.
|
145
|
+
#
|
146
|
+
# @see Resource#save!
|
147
|
+
#
|
148
|
+
# @param [Resource] resource the resource that you want to set at the specified
|
149
|
+
# identifier. If an object exists at that identifier already it will be
|
150
|
+
# replaced.
|
151
|
+
#
|
152
|
+
def set_object(resource)
|
153
|
+
objects[resource.identifier] = resource.properties
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# @note removing an item from the regitry does not remove all references
|
158
|
+
# to the item within the project. At this time, this could leave resources
|
159
|
+
# with references to resources that are invalid.
|
160
|
+
#
|
161
|
+
# @param [String] identifier of the object to remove from the registry.
|
162
|
+
#
|
163
|
+
def remove_object(identifier)
|
164
|
+
objects.delete identifier
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|