zimt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use system
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in zimt.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :default => :spec
4
+ task :spec do
5
+ require 'bacon'
6
+ Bacon.summary_on_exit
7
+ Dir.glob(File.join(File.dirname(__FILE__),'spec','*_spec.rb')).each {|f| load(f) }
8
+ end
data/bin/zimt ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'zimt'
4
+
5
+ Zimt::CLI.start
6
+
data/lib/zimt.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+ require 'open-uri'
4
+ require 'pathname'
5
+ require 'thor'
6
+ require "zimt/cli"
7
+ require "zimt/pbxproj"
8
+ require "zimt/sprinkle"
9
+ require "zimt/version"
10
+
11
+ module Zimt
12
+ def self.xcodeproj
13
+ @xcodeproj ||= find_xcodeproj
14
+ raise "Could not locate .xcodeproj" unless @xcodeproj
15
+ Pathname.new(@xcodeproj)
16
+ end
17
+
18
+ def self.pbxproj
19
+ path = Pathname.new(File.join(self.xcodeproj, 'project.pbxproj'))
20
+ @pbxproj ||= PBXProj.new(path)
21
+ end
22
+
23
+ def self.zimts_dir
24
+ Pathname.new(File.expand_path(File.join(self.xcodeproj, '..', 'zimts')))
25
+ end
26
+
27
+ private
28
+
29
+ def self.find_xcodeproj
30
+ given = ENV['ZIMT_XCODEPROJ']
31
+ return given if given && !given.empty?
32
+
33
+ previous = nil
34
+ current = File.expand_path(Dir.pwd)
35
+
36
+ until !File.directory?(current) || current == previous
37
+ # otherwise return the Gemfile if it's there
38
+ filenames = Dir.glob(File.join(current, '*.xcodeproj'))
39
+ raise "More than one .xcodeproj found: #{filenames}" if filenames.length > 1
40
+ filename = filenames.first
41
+ return filename if File.directory?(filename) and File.file?(File.join(filename, 'project.pbxproj'))
42
+ current, previous = File.expand_path("..", current), current
43
+ end
44
+ end
45
+
46
+ end
47
+
data/lib/zimt/cli.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Zimt
2
+ class CLI < Thor
3
+ desc "add SPRINKLE", "Adds the sprinkle"
4
+ def add(name)
5
+ sprinkle = Sprinkle.get(name)
6
+ sprinkle.install
7
+ rescue OpenURI::HTTPError
8
+ puts 'Sprinkle not found'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,300 @@
1
+ module Zimt
2
+ class PBXProj
3
+ def self.plutil(file)
4
+ `plutil -convert json -o - #{file}`
5
+ end
6
+
7
+ def randhex(length=1)
8
+ @buffer = ""
9
+ length.times do
10
+ @buffer << rand(16).to_s(16).upcase
11
+ end
12
+ @buffer
13
+ end
14
+
15
+ # Documentation on PBXObjectId by @tjw
16
+ # http://lists.apple.com/archives/projectbuilder-users/2003/Jan/msg00263.html
17
+ #
18
+ # ObjectId gets generated in the following format:
19
+ # +----------------------------------------------------------------------+
20
+ # | RANDOM | SEQ | TIME | PID | IP (2) |
21
+ # | byte byte | byte byte | byte byte byte byte | byte byte | byte byte |
22
+ # +----------------------------------------------------------------------+
23
+ # RANDOM = 2 bytes of random number for distribution
24
+ # SEQ = Unsigned short sequence counter that starts randomly.
25
+ # TIME = Seconds since epoch (1/1/1970)
26
+ # PID = Process ID
27
+ # This is only two bytes even though most pids are longs.
28
+ # Here you would take the lower 2 bytes.
29
+ # IP = IP Address of the machine (subnet.hostId)
30
+ def uuid
31
+ # TODO
32
+ @prefix ||= randhex(4)
33
+ @suffix ||= Time.now.to_i.to_s(16).upcase + randhex(8)
34
+ @count ||= rand(16**4)
35
+ @count += 1
36
+ if @count.to_s(16).length < 4
37
+ @count = @count * 16
38
+ end
39
+ uuid = "#{@prefix}#{@count.to_s(16).upcase}#{@suffix}"
40
+ if uuid.length != 24
41
+ puts "uuid length wrong: #{uuid}"
42
+ exit
43
+ end
44
+ uuid
45
+ end
46
+
47
+ attr_reader :content, :hash, :objects, :root
48
+ attr_accessor :position
49
+
50
+ def initialize(file)
51
+ @filename = file
52
+ @content = File.readlines(file)
53
+ self.parse
54
+ end
55
+
56
+ def parse
57
+ @hash = JSON.parse(PBXProj.plutil(@filename)).freeze
58
+ @objects = @hash['objects']
59
+ @root = PBXHash.new(self, @hash['rootObject'], @objects[@hash['rootObject']])
60
+ end
61
+
62
+ def save!
63
+ File.open(@filename, "w") { |f|
64
+ f.write(self.content.join(''))
65
+ }
66
+ end
67
+
68
+ def zimt_group
69
+ self.root.mainGroup.children.select{ |g| g.path == 'Zimt' }.first
70
+ end
71
+
72
+ def ensure_zimt_group
73
+ zimt_group = self.zimt_group
74
+ if zimt_group.nil?
75
+ self.add_zimt_group
76
+ end
77
+ end
78
+
79
+ # C5FE9B6F13BA7537004CCA66 = {
80
+ # isa = PBXGroup;
81
+ # children = (
82
+ # C5FE9B8413BA7537004CCA66 /* Sources */,
83
+ # C7826AD313D9137D00661EEC /* Resources */,
84
+ # C5FE9B7D13BA7537004CCA66 /* Frameworks */,
85
+ # C5FE9B7B13BA7537004CCA66 /* Products */,
86
+ # C5E20A8613F4507D00C5DDF3 /* Zimt */,
87
+ # );
88
+ # sourceTree = "<group>";
89
+ # };
90
+ def add_zimt_group
91
+ # Add Zimt reference to mainGroup
92
+ groupid = self.root.mainGroup.pbxid
93
+ scan_to "\t\t#{groupid}"
94
+ scan_to "\t\t\t);"
95
+ newgroup = self.uuid
96
+ self.content.insert(@position, "\t\t\t\t#{newgroup} /* Zimt */,\n")
97
+
98
+ # Find position for Zimt reference in PBXGRoup section
99
+ @position = 0
100
+ scan_to "/* Begin PBXGroup section */"
101
+ begin_position = @position
102
+ scan_to "/* End PBXGroup section */"
103
+ end_position = @position
104
+
105
+ @position = begin_position
106
+ while @position < end_position
107
+ line = self.content[@position]
108
+ if (line.end_with? " = {\n")
109
+ groupname = line.split(' ')[0]
110
+ if groupname > newgroup
111
+ break
112
+ end
113
+ end
114
+ @position += 1
115
+ end
116
+
117
+ # Add Zimt Group
118
+ self.content.insert(@position,
119
+ "\t\t#{newgroup} /* Zimt */ = {\n",
120
+ "\t\t\tisa = PBXGroup;\n",
121
+ "\t\t\tchildren = (\n",
122
+ "\t\t\t);\n",
123
+ "\t\t\tpath = Zimt;\n",
124
+ "\t\t\tsourceTree = \"<group>\";\n",
125
+ "\t\t};\n")
126
+
127
+ self.save!
128
+ self.parse
129
+ end
130
+
131
+ #/* Begin PBXFileReference section */
132
+ # C5D0CB021406C3AA002E631F /* Hans.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hans.h; sourceTree = "<group>"; };
133
+ #/* End PBXFileReference section */
134
+ def add_file(file, file_type)
135
+ zimt_group_id = self.zimt_group.pbxid
136
+ # Add file to Zimt group
137
+ groupid = self.root.mainGroup.pbxid
138
+ scan_to "\t\t#{zimt_group_id}"
139
+ scan_to "\t\t\t);"
140
+ newgroup = self.uuid
141
+ self.content.insert(@position, "\t\t\t\t#{newgroup} /* #{file} */,\n")
142
+
143
+ # Find position for Zimt reference in PBXGRoup section
144
+ @position = 0
145
+ scan_to "/* Begin PBXFileReference section */"
146
+ begin_position = @position
147
+ scan_to "/* End PBXFileReference section */"
148
+ end_position = @position
149
+
150
+ @position = begin_position
151
+ while @position < end_position
152
+ line = self.content[@position]
153
+ groupname = line.split(' ')[0]
154
+ if groupname > newgroup
155
+ break
156
+ end
157
+ @position += 1
158
+ end
159
+
160
+ # Add Zimt Group
161
+ self.content.insert(@position,
162
+ "\t\t#{newgroup} /* #{file} */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = #{file_type}; path = #{file}; sourceTree = \"<group>\"; };\n")
163
+
164
+ self.save!
165
+ self.parse
166
+ return newgroup
167
+ end
168
+
169
+ def add_h_file(file)
170
+ add_file(file, "sourcecode.c.h")
171
+ end
172
+
173
+ def add_m_file(file)
174
+ fileref = self.add_file(file, "sourcecode.c.objc")
175
+ buildfileref = self.add_buildfile(file, fileref)
176
+ add_buildfileref_to_build_phase(file, buildfileref)
177
+ end
178
+
179
+ #/* Begin PBXBuildFile section */
180
+ # C53D93B21406F98300F4CDDE /* Hans.m in Sources */ = {isa = PBXBuildFile; fileRef = C53D93B11406F98300F4CDDE /* Hans.m */; };
181
+ #/* End PBXBuildFile section */
182
+ def add_buildfile(file, fileref)
183
+ newgroup = self.uuid
184
+ # Find position for Zimt reference in PBXGRoup section
185
+ @position = 0
186
+ scan_to "/* Begin PBXBuildFile section */"
187
+ begin_position = @position
188
+ scan_to "/* End PBXBuildFile section */"
189
+ end_position = @position
190
+
191
+ @position = begin_position
192
+ while @position < end_position
193
+ line = self.content[@position]
194
+ groupname = line.split(' ')[0]
195
+ if groupname > newgroup
196
+ break
197
+ end
198
+ @position += 1
199
+ end
200
+
201
+ # Add Zimt Group
202
+ self.content.insert(@position,
203
+ "\t\t#{newgroup} /* #{file} in Sources */ = {isa = PBXBuildFile; fileRef = #{fileref} /* #{file} */; };\n")
204
+
205
+ self.save!
206
+ self.parse
207
+ return newgroup
208
+ end
209
+
210
+ #/* Begin PBXSourcesBuildPhase section */
211
+ # 8D11072C0486CEB800E47090 /* Sources */ = {
212
+ # files = (
213
+ # C53D93B21406F98300F4CDDE /* Hans.m in Sources */,
214
+ # );
215
+ # C56D96C81385E71800070608 /* Sources */ = {
216
+ #/* End PBXSourcesBuildPhase section */
217
+ def add_buildfileref_to_build_phase(file, buildfileref)
218
+ @position = 0
219
+ scan_to "/* Begin PBXSourcesBuildPhase section */"
220
+ begin_position = @position
221
+ scan_to "/* End PBXSourcesBuildPhase section */"
222
+ end_position = @position
223
+
224
+ @position = begin_position
225
+ while @position < end_position
226
+ line = self.content[@position]
227
+ if (line.end_with? " = {\n")
228
+ old_position = @position
229
+ scan_to(" );\n")
230
+ self.content.insert(@position,
231
+ " #{buildfileref} /* #{file} in Sources */,\n")
232
+ @position = old_position + 1 # offset for added line
233
+ break
234
+ end
235
+ @position += 1
236
+ end
237
+
238
+ self.save!
239
+ self.parse
240
+ end
241
+
242
+ def scan_to(what)
243
+ @position ||= 0
244
+ while true
245
+ line = self.content[@position]
246
+ if line.nil?
247
+ raise "#{what} not found"
248
+ end
249
+ if line.start_with? what
250
+ return
251
+ end
252
+ @position += 1
253
+ end
254
+ end
255
+
256
+ def current_line
257
+ @position ||= 0
258
+ self.content[@position]
259
+ end
260
+ end
261
+
262
+ class PBXHash
263
+ attr_reader :pbxid
264
+
265
+ def initialize(pbxproj, pbxid, node)
266
+ @pbxproj = pbxproj
267
+ @pbxid = pbxid
268
+ @node = node
269
+ end
270
+
271
+ def keys
272
+ @node.keys
273
+ end
274
+
275
+ def inspect
276
+ "<PBX: #{keys.join(', ')}>"
277
+ end
278
+
279
+ private
280
+ def wrap(raw, recurse=true, id=nil)
281
+ if raw.is_a? Array
282
+ raw.map { |i| wrap(i, recurse) }
283
+ elsif raw.is_a? Hash
284
+ new_hash = raw.inject({}) { |h,(k,v)| h[k] = wrap(v, recurse) ; h }
285
+ PBXHash.new(@pbxproj, id, new_hash)
286
+ else
287
+ if recurse and @pbxproj.objects.include? raw
288
+ wrap(@pbxproj.objects[raw], false, raw)
289
+ else
290
+ raw
291
+ end
292
+ end
293
+ end
294
+
295
+ def method_missing(sym)
296
+ wrap(@node[sym.to_s])
297
+ end
298
+
299
+ end
300
+ end
@@ -0,0 +1,41 @@
1
+ module Zimt
2
+ class Sprinkle
3
+ attr_accessor :name, :url, :files
4
+
5
+ def self.get(name)
6
+ if name.start_with?('http://') || name.start_with?('https://')
7
+ url = name
8
+ else
9
+ url = "https://raw.github.com/zimt/sprinkles/stable/#{name.downcase}.sprinkle.yml"
10
+ end
11
+ spec = open(url) { |f| YAML::load(f) }
12
+ self.new(spec)
13
+ end
14
+
15
+ def initialize(spec)
16
+ @spec = spec
17
+ @name = spec["name"]
18
+ @url = spec["url"]
19
+ @files = spec["files"]
20
+ end
21
+
22
+ def install
23
+ puts "Installing #{name}"
24
+ FileUtils.mkdir "Zimt"
25
+ Zimt.pbxproj.ensure_zimt_group
26
+ files.each do |url|
27
+ file = Pathname.new(URI.parse(url).path).basename('.sprinkle.yml').to_s
28
+ puts "Adding #{file}..."
29
+ open(Pathname.new("Zimt").join(file), "w") do |io|
30
+ io.write(open(url).read)
31
+ end
32
+ if file.end_with? ".m"
33
+ Zimt.pbxproj.add_m_file(file)
34
+ else
35
+ Zimt.pbxproj.add_h_file(file)
36
+ end
37
+ end
38
+ puts "All done"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Zimt
2
+ VERSION = "0.0.1"
3
+ end
data/zimt.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "zimt/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "zimt"
7
+ s.version = Zimt::VERSION
8
+ s.authors = ["Martin Schürrer"]
9
+ s.email = ["martin@schuerrer.org"]
10
+ s.homepage = "https://github.com/zimt/zimt"
11
+ s.summary = "Zimt is a collection of Cocoa extensions with clever package management"
12
+ s.description = "Zimt downloads and adds files to your .xcodeproj."
13
+
14
+ s.rubyforge_project = "zimt"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'json', '~> 1.5.0'
22
+ s.add_dependency 'thor', '~> 0.14.0'
23
+ s.add_development_dependency 'bacon', '~> 1.1.0'
24
+ s.add_development_dependency 'ruby-debug', '~> 0.10.0'
25
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zimt
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - "Martin Schu\xCC\x88rrer"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-08-29 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ version_requirements: &id001 !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ~>
24
+ - !ruby/object:Gem::Version
25
+ segments:
26
+ - 1
27
+ - 5
28
+ - 0
29
+ version: 1.5.0
30
+ requirement: *id001
31
+ name: json
32
+ prerelease: false
33
+ type: :runtime
34
+ - !ruby/object:Gem::Dependency
35
+ version_requirements: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ - 14
42
+ - 0
43
+ version: 0.14.0
44
+ requirement: *id002
45
+ name: thor
46
+ prerelease: false
47
+ type: :runtime
48
+ - !ruby/object:Gem::Dependency
49
+ version_requirements: &id003 !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 1
55
+ - 1
56
+ - 0
57
+ version: 1.1.0
58
+ requirement: *id003
59
+ name: bacon
60
+ prerelease: false
61
+ type: :development
62
+ - !ruby/object:Gem::Dependency
63
+ version_requirements: &id004 !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ - 10
70
+ - 0
71
+ version: 0.10.0
72
+ requirement: *id004
73
+ name: ruby-debug
74
+ prerelease: false
75
+ type: :development
76
+ description: Zimt downloads and adds files to your .xcodeproj.
77
+ email:
78
+ - martin@schuerrer.org
79
+ executables:
80
+ - zimt
81
+ extensions: []
82
+
83
+ extra_rdoc_files: []
84
+
85
+ files:
86
+ - .gitignore
87
+ - .rvmrc
88
+ - Gemfile
89
+ - Rakefile
90
+ - bin/zimt
91
+ - lib/zimt.rb
92
+ - lib/zimt/cli.rb
93
+ - lib/zimt/pbxproj.rb
94
+ - lib/zimt/sprinkle.rb
95
+ - lib/zimt/version.rb
96
+ - zimt.gemspec
97
+ has_rdoc: true
98
+ homepage: https://github.com/zimt/zimt
99
+ licenses: []
100
+
101
+ post_install_message:
102
+ rdoc_options: []
103
+
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ segments:
118
+ - 0
119
+ version: "0"
120
+ requirements: []
121
+
122
+ rubyforge_project: zimt
123
+ rubygems_version: 1.3.6
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: Zimt is a collection of Cocoa extensions with clever package management
127
+ test_files: []
128
+