passbookpgh 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +7 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +130 -0
  5. data/LICENSE +22 -0
  6. data/README.md +294 -0
  7. data/Rakefile +36 -0
  8. data/VERSION +1 -0
  9. data/bin/pk +22 -0
  10. data/lib/commands/build.rb +62 -0
  11. data/lib/commands/commands.rb +31 -0
  12. data/lib/commands/generate.rb +44 -0
  13. data/lib/commands/templates/boarding-pass.json +56 -0
  14. data/lib/commands/templates/coupon.json +33 -0
  15. data/lib/commands/templates/event-ticket.json +33 -0
  16. data/lib/commands/templates/generic.json +33 -0
  17. data/lib/commands/templates/store-card.json +33 -0
  18. data/lib/passbook/pkpass.rb +121 -0
  19. data/lib/passbook/push_notification.rb +19 -0
  20. data/lib/passbook/signer.rb +40 -0
  21. data/lib/passbook/version.rb +3 -0
  22. data/lib/passbook.rb +15 -0
  23. data/lib/rack/passbook_rack.rb +98 -0
  24. data/lib/rails/generators/passbook/config/config_generator.rb +16 -0
  25. data/lib/rails/generators/passbook/config/templates/initializer.rb +13 -0
  26. data/lib/utils/command_utils.rb +12 -0
  27. data/passbookpgh.gemspec +110 -0
  28. data/spec/data/icon.png +0 -0
  29. data/spec/data/icon@2x.png +0 -0
  30. data/spec/data/logo.png +0 -0
  31. data/spec/data/logo@2x.png +0 -0
  32. data/spec/lib/commands/build_spec.rb +92 -0
  33. data/spec/lib/commands/commands_spec.rb +102 -0
  34. data/spec/lib/commands/commands_spec_helper.rb +69 -0
  35. data/spec/lib/commands/generate_spec.rb +72 -0
  36. data/spec/lib/passbook/pkpass_spec.rb +108 -0
  37. data/spec/lib/passbook/push_notification_spec.rb +23 -0
  38. data/spec/lib/passbook/signer_spec.rb +84 -0
  39. data/spec/lib/rack/passbook_rack_spec.rb +233 -0
  40. data/spec/spec_helper.rb +9 -0
  41. metadata +244 -0
@@ -0,0 +1,56 @@
1
+ {
2
+ "formatVersion" : 1,
3
+ "passTypeIdentifier" : "pass.com.example.boarding-pass",
4
+ "description" : "Example Boarding Pass",
5
+ "teamIdentifier": "Example",
6
+ "organizationName": "Example",
7
+ "serialNumber" : "123456",
8
+ "foregroundColor": "#866B23",
9
+ "backgroundColor": "#FFD248",
10
+ "boardingPass" : {
11
+ "primaryFields" : [
12
+ {
13
+ "key" : "origin",
14
+ "label" : "Atlanta",
15
+ "value" : "ATL"
16
+ },
17
+ {
18
+ "key" : "destination",
19
+ "label" : "Johannesburg",
20
+ "value" : "JNB"
21
+ }
22
+ ],
23
+ "secondaryFields" : [
24
+ {
25
+ "key" : "boarding-gate",
26
+ "label" : "Gate",
27
+ "value" : "F12"
28
+ }
29
+ ],
30
+ "auxiliaryFields" : [
31
+ {
32
+ "key" : "seat",
33
+ "label" : "Seat",
34
+ "value" : "7A"
35
+ },
36
+ {
37
+ "key" : "passenger-name",
38
+ "label" : "Passenger",
39
+ "value" : "Honey Badger"
40
+ }
41
+ ],
42
+ "transitType" : "PKTransitTypeAir",
43
+ "barcode" : {
44
+ "message" : "DL123",
45
+ "format" : "PKBarcodeFormatQR",
46
+ "messageEncoding" : "iso-8859-1"
47
+ },
48
+ "backFields" : [
49
+ {
50
+ "key" : "terms",
51
+ "label" : "Terms and Conditions",
52
+ "value" : "Valid for date of travel only"
53
+ }
54
+ ]
55
+ }
56
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "formatVersion" : 1,
3
+ "passTypeIdentifier" : "pass.com.example.coupon",
4
+ "description" : "Example Coupon",
5
+ "teamIdentifier": "Example",
6
+ "organizationName": "Example",
7
+ "serialNumber" : "123456",
8
+ "foregroundColor": "#FFFFFF",
9
+ "backgroundColor": "#C799FF",
10
+ "generic" : {
11
+ "primaryFields" : [
12
+
13
+ ],
14
+ "secondaryFields" : [
15
+
16
+ ],
17
+ "auxiliaryFields" : [
18
+
19
+ ],
20
+ "barcode" : {
21
+ "message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
22
+ "format" : "PKBarcodeFormatPDF417",
23
+ "messageEncoding" : "iso-8859-1"
24
+ },
25
+ "backFields" : [
26
+ {
27
+ "key" : "terms",
28
+ "label" : "Terms and Conditions",
29
+ "value" : "T's and C's Apply"
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "formatVersion" : 1,
3
+ "passTypeIdentifier" : "pass.com.example.event-ticket",
4
+ "description" : "Example Event Ticket",
5
+ "teamIdentifier": "Example",
6
+ "organizationName": "Example",
7
+ "serialNumber" : "123456",
8
+ "foregroundColor": "#FFFFFF",
9
+ "backgroundColor": "#FF5453",
10
+ "generic" : {
11
+ "primaryFields" : [
12
+
13
+ ],
14
+ "secondaryFields" : [
15
+
16
+ ],
17
+ "auxiliaryFields" : [
18
+
19
+ ],
20
+ "barcode" : {
21
+ "message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
22
+ "format" : "PKBarcodeFormatPDF417",
23
+ "messageEncoding" : "iso-8859-1"
24
+ },
25
+ "backFields" : [
26
+ {
27
+ "key" : "terms",
28
+ "label" : "Terms and Conditions",
29
+ "value" : "T's and C's apply"
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "formatVersion" : 1,
3
+ "passTypeIdentifier" : "pass.com.example.generic",
4
+ "description" : "Example Generic Pass",
5
+ "teamIdentifier": "Example",
6
+ "organizationName": "Example",
7
+ "serialNumber" : "123456",
8
+ "foregroundColor": "#FFFFFF",
9
+ "backgroundColor": "#444444",
10
+ "generic" : {
11
+ "primaryFields" : [
12
+
13
+ ],
14
+ "secondaryFields" : [
15
+
16
+ ],
17
+ "auxiliaryFields" : [
18
+
19
+ ],
20
+ "barcode" : {
21
+ "message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
22
+ "format" : "PKBarcodeFormatPDF417",
23
+ "messageEncoding" : "iso-8859-1"
24
+ },
25
+ "backFields" : [
26
+ {
27
+ "key" : "terms",
28
+ "label" : "Terms and Conditions",
29
+ "value" : "Put your terms here"
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "formatVersion" : 1,
3
+ "passTypeIdentifier" : "pass.com.example.store-card",
4
+ "description" : "Example Store Card",
5
+ "teamIdentifier": "Example",
6
+ "organizationName": "Example",
7
+ "serialNumber" : "123456",
8
+ "foregroundColor": "#FFFFFF",
9
+ "backgroundColor": "#AFC1E3",
10
+ "generic" : {
11
+ "primaryFields" : [
12
+
13
+ ],
14
+ "secondaryFields" : [
15
+
16
+ ],
17
+ "auxiliaryFields" : [
18
+
19
+ ],
20
+ "barcode" : {
21
+ "message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
22
+ "format" : "PKBarcodeFormatPDF417",
23
+ "messageEncoding" : "iso-8859-1"
24
+ },
25
+ "backFields" : [
26
+ {
27
+ "key" : "terms",
28
+ "label" : "Terms and Conditions",
29
+ "value" : "T's and C's apply"
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,121 @@
1
+ require 'digest/sha1'
2
+ require 'openssl'
3
+ require 'zip'
4
+ require 'base64'
5
+
6
+ module Passbook
7
+ class PKPass
8
+ attr_accessor :pass, :manifest_files, :signer
9
+
10
+ TYPES = ['boarding-pass', 'coupon', 'event-ticket', 'store-card', 'generic']
11
+
12
+ def initialize(pass, init_signer = nil)
13
+ @pass = pass
14
+ @manifest_files = []
15
+ @signer = init_signer || Passbook::Signer.new
16
+ end
17
+
18
+ def addFile(file)
19
+ @manifest_files << file
20
+ end
21
+
22
+ def addFiles(files)
23
+ @manifest_files += files
24
+ end
25
+
26
+ # for backwards compatibility
27
+ def json=(json)
28
+ @pass = json
29
+ end
30
+
31
+ def build
32
+ manifest = createManifest
33
+
34
+ # Check pass for necessary files and fields
35
+ checkPass manifest
36
+
37
+ # Create pass signature
38
+ signature = @signer.sign manifest
39
+
40
+ [manifest, signature]
41
+ end
42
+
43
+ # Backward compatibility
44
+ def create
45
+ self.file.path
46
+ end
47
+
48
+ # Return a Tempfile containing our ZipStream
49
+ def file(options = {})
50
+ options[:file_name] ||= 'pass.pkpass'
51
+
52
+ temp_file = Tempfile.new(options[:file_name])
53
+ temp_file.binmode
54
+ temp_file.write self.stream.string
55
+ temp_file.close
56
+
57
+ temp_file
58
+ end
59
+
60
+ # Return a ZipOutputStream
61
+ def stream
62
+ manifest, signature = build
63
+
64
+ outputZip manifest, signature
65
+ end
66
+
67
+ private
68
+
69
+ def checkPass(manifest)
70
+ # Check for default images
71
+ raise 'Icon missing' unless manifest.include?('icon.png')
72
+ raise 'Icon@2x missing' unless manifest.include?('icon@2x.png')
73
+
74
+ # Check for developer field in JSON
75
+ raise 'Pass Type Identifier missing' unless @pass.include?('passTypeIdentifier')
76
+ raise 'Team Identifier missing' unless @pass.include?('teamIdentifier')
77
+ raise 'Serial Number missing' unless @pass.include?('serialNumber')
78
+ raise 'Organization Name Identifier missing' unless @pass.include?('organizationName')
79
+ raise 'Format Version' unless @pass.include?('formatVersion')
80
+ raise 'Format Version should be a numeric' unless JSON.parse(@pass)['formatVersion'].is_a?(Numeric)
81
+ raise 'Description' unless @pass.include?('description')
82
+ end
83
+
84
+ def createManifest
85
+ sha1s = {}
86
+ sha1s['pass.json'] = Digest::SHA1.hexdigest @pass
87
+
88
+ @manifest_files.each do |file|
89
+ if file.class == Hash
90
+ sha1s[file[:name]] = Digest::SHA1.hexdigest file[:content]
91
+ else
92
+ sha1s[File.basename(file)] = Digest::SHA1.file(file).hexdigest
93
+ end
94
+ end
95
+
96
+ sha1s.to_json
97
+ end
98
+
99
+ def outputZip(manifest, signature)
100
+
101
+ Zip::OutputStream.write_buffer do |zip|
102
+ zip.put_next_entry 'pass.json'
103
+ zip.write @pass
104
+ zip.put_next_entry 'manifest.json'
105
+ zip.write manifest
106
+ zip.put_next_entry 'signature'
107
+ zip.write signature
108
+
109
+ @manifest_files.each do |file|
110
+ if file.class == Hash
111
+ zip.put_next_entry file[:name]
112
+ zip.print file[:content]
113
+ else
114
+ zip.put_next_entry File.basename(file)
115
+ zip.print IO.read(file)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,19 @@
1
+ module Passbook
2
+ class PushNotification
3
+ class << self
4
+ def pusher
5
+ @pusher ||= Grocer.pusher(
6
+ :certificate => Passbook.notification_cert,
7
+ :passphrase => Passbook.notification_passphrase || "",
8
+ :gateway => Passbook.notification_gateway
9
+ )
10
+ end
11
+
12
+ def send_notification(device_token)
13
+ notification = Grocer::PassbookNotification.new(:device_token => device_token)
14
+
15
+ pusher.push notification
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Passbook
5
+ class Signer
6
+ attr_accessor :certificate, :password, :key, :wwdc_cert, :key_hash, :p12_cert
7
+
8
+ def initialize(params = {})
9
+ @certificate = params[:certificate] || Passbook.p12_certificate
10
+ @password = params[:password] || Passbook.p12_password
11
+ @key = params[:key] || (params.empty? ? Passbook.p12_key : nil)
12
+ @wwdc_cert = params[:wwdc_cert] || Passbook.wwdc_cert
13
+ compute_cert
14
+ end
15
+
16
+ def sign(data)
17
+ wwdc = OpenSSL::X509::Certificate.new File.read(wwdc_cert)
18
+ pk7 = OpenSSL::PKCS7.sign key_hash[:cert], key_hash[:key], data.to_s, [wwdc], OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::DETACHED
19
+ data = OpenSSL::PKCS7.write_smime pk7
20
+
21
+ str_debut = "filename=\"smime.p7s\"\n\n"
22
+ data = data[data.index(str_debut)+str_debut.length..data.length-1]
23
+ str_end = "\n\n------"
24
+ data = data[0..data.index(str_end)-1]
25
+
26
+ Base64.decode64(data)
27
+ end
28
+
29
+ def compute_cert
30
+ @key_hash = {}
31
+ if key
32
+ @key_hash[:key] = OpenSSL::PKey::RSA.new File.read(key), password
33
+ @key_hash[:cert] = OpenSSL::X509::Certificate.new File.read(certificate)
34
+ else
35
+ p12 = OpenSSL::PKCS12.new File.read(certificate), password
36
+ @key_hash[:key], @key_hash[:cert] = p12.key, p12.certificate
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Passbook
2
+ VERSION = "0.4.2"
3
+ end
data/lib/passbook.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "passbook/version"
2
+ require "passbook/pkpass"
3
+ require "passbook/signer"
4
+ require 'active_support/core_ext/module/attribute_accessors'
5
+ require 'passbook/push_notification'
6
+ require 'grocer'
7
+ require 'rack/passbook_rack'
8
+
9
+ module Passbook
10
+ mattr_accessor :p12_certificate, :p12_password, :wwdc_cert, :p12_key, :notification_cert, :notification_gateway, :notification_passphrase
11
+
12
+ def self.configure
13
+ yield self
14
+ end
15
+ end
@@ -0,0 +1,98 @@
1
+ module Rack
2
+ class PassbookRack
3
+
4
+ def initialize(app)
5
+ @app = app
6
+ @parameters = {}
7
+ end
8
+
9
+ def call(env)
10
+ @parameters['authToken'] = env['HTTP_AUTHORIZATION'].gsub(/ApplePass /,'') if env['HTTP_AUTHORIZATION']
11
+ @parameters.merge!(Rack::Utils.parse_nested_query(env['QUERY_STRING']))
12
+ method_and_params = find_method env['PATH_INFO']
13
+ if method_and_params
14
+ case method_and_params[:method]
15
+ when 'device_register_delete'
16
+ if env['REQUEST_METHOD'] == 'POST'
17
+ [Passbook::PassbookNotification.
18
+ register_pass(method_and_params[:params].merge! JSON.parse(env['rack.input'].read 1000))[:status],
19
+ {}, ['']]
20
+ elsif env['REQUEST_METHOD'] == 'DELETE'
21
+ [Passbook::PassbookNotification.unregister_pass(method_and_params[:params])[:status], {}, {}]
22
+ end
23
+ when 'passes_for_device'
24
+ response = Passbook::PassbookNotification.passes_for_device(method_and_params[:params])
25
+ [response ? 200 : 204, {}, [response.to_json]]
26
+ when 'latest_pass'
27
+ response = Passbook::PassbookNotification.latest_pass(method_and_params[:params])
28
+ if response[:status] == 200
29
+ [200, {'Content-Type' => 'application/vnd.apple.pkpass',
30
+ 'Content-Disposition' => 'attachment',
31
+ 'filename' => "#{method_and_params[:params]['serialNumber']}.pkpass","last-modified" => response[:last_modified]}, [response[:latest_pass]]]
32
+ else
33
+ [response[:status], {}, {}]
34
+ end
35
+ when 'log'
36
+ Passbook::PassbookNotification.passbook_log JSON.parse(env['rack.input'].read 10000)
37
+ [200, {}, {}]
38
+ else
39
+ end
40
+ else
41
+ @app.call env
42
+ end
43
+ end
44
+
45
+ def append_parameter_separator url
46
+ end
47
+
48
+ def each(&block)
49
+ end
50
+
51
+
52
+ def find_method(path)
53
+ parsed_path = path.split '/'
54
+ url_beginning = parsed_path.index 'v1'
55
+ if url_beginning
56
+ args_length = parsed_path.size - url_beginning
57
+
58
+ if (parsed_path[url_beginning + 1 ] == 'devices') and
59
+ (parsed_path[url_beginning + 3 ] == 'registrations')
60
+ if args_length == 6
61
+ return method_and_params_hash 'device_register_delete', path
62
+ elsif args_length == 5
63
+ return method_and_params_hash 'passes_for_device', path
64
+ end
65
+ elsif parsed_path[url_beginning + 1] == 'passes' and args_length == 4
66
+ return method_and_params_hash 'latest_pass', path
67
+ elsif parsed_path[url_beginning + 1] == 'log' and args_length == 2
68
+ return {:method => 'log'}
69
+ end
70
+ end
71
+
72
+ return nil
73
+ end
74
+
75
+ private
76
+
77
+ def method_and_params_hash(method, path)
78
+ parsed_path = path.split '/'
79
+ url_beginning = parsed_path.index 'v1'
80
+ if method == 'latest_pass'
81
+ {:method => 'latest_pass',
82
+ :params => @parameters.merge!({'passTypeIdentifier' => parsed_path[url_beginning + 2],
83
+ 'serialNumber' => parsed_path[url_beginning + 3]})}
84
+ else
85
+ return_hash = {:method => method, :params =>
86
+ @parameters.merge!({'deviceLibraryIdentifier' => parsed_path[url_beginning + 2],
87
+ 'passTypeIdentifier' => parsed_path[url_beginning + 4]})}
88
+ if method == 'device_register_delete'
89
+ return_hash[:params]['serialNumber'] = parsed_path[url_beginning + 5]
90
+ end
91
+ return_hash
92
+ end
93
+ end
94
+
95
+
96
+ end
97
+ end
98
+
@@ -0,0 +1,16 @@
1
+ module Passbook
2
+ module Generators
3
+ class ConfigGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ argument :wwdc_cert_path, type: :string, default: '', optional: true, banner: "Absolute path to your wwdc cert file"
7
+ argument :p12_cert_path, type: :string, default: '', optional: true, banner: "Absolute path to your cert.p12 file"
8
+ argument :p12_password, type: :string, default: '', optional: true, banner: "Password for your certificate"
9
+
10
+ desc 'Create passbook initializer'
11
+ def create_initializer_file
12
+ template 'initializer.rb', File.join('config', 'initializers', 'passbook.rb')
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ require 'passbook'
2
+
3
+ Passbook.configure do |passbook|
4
+
5
+ # Path to your wwdc cert file
6
+ passbook.wwdc_cert = '<%= wwdc_cert_path %>'
7
+
8
+ # Path to your cert.p12 file
9
+ passbook.p12_certificate = '<%= p12_cert_path %>'
10
+
11
+ # Password for your certificate
12
+ passbook.p12_password = '<%= p12_password %>'
13
+ end
@@ -0,0 +1,12 @@
1
+ # this was added for testability because I couldn't figure out something better.
2
+ class CommandUtils
3
+ def self.get_assets(directory)
4
+ Dir[File.join(directory, '*')]
5
+ end
6
+
7
+ def self.get_current_directory
8
+ File.dirname(__FILE__)
9
+ end
10
+ end
11
+
12
+
@@ -0,0 +1,110 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: passbook 0.4.4 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "passbookpgh"
9
+ s.version = "0.4.5"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Thomas Lauro", "Lance Gleason", "Danielle Greaves"]
14
+ s.date = "2022-01-26"
15
+ s.description = "This gem allows you to create IOS Passbooks. Unlike some, this works with Rails but does not require it."
16
+ s.email = ["thomas@lauro.fr", "lgleason@polyglotprogramminginc.com", "greaves@trustarts.org"]
17
+ s.executables = ["pk"]
18
+ s.extra_rdoc_files = [
19
+ "LICENSE",
20
+ "README.md"
21
+ ]
22
+ s.files = [
23
+ ".travis.yml",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE",
27
+ "README.md",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "bin/pk",
31
+ "lib/commands/build.rb",
32
+ "lib/commands/commands.rb",
33
+ "lib/commands/generate.rb",
34
+ "lib/commands/templates/boarding-pass.json",
35
+ "lib/commands/templates/coupon.json",
36
+ "lib/commands/templates/event-ticket.json",
37
+ "lib/commands/templates/generic.json",
38
+ "lib/commands/templates/store-card.json",
39
+ "lib/passbook.rb",
40
+ "lib/passbook/pkpass.rb",
41
+ "lib/passbook/push_notification.rb",
42
+ "lib/passbook/signer.rb",
43
+ "lib/passbook/version.rb",
44
+ "lib/rack/passbook_rack.rb",
45
+ "lib/rails/generators/passbook/config/config_generator.rb",
46
+ "lib/rails/generators/passbook/config/templates/initializer.rb",
47
+ "lib/utils/command_utils.rb",
48
+ "passbookpgh.gemspec",
49
+ "spec/data/icon.png",
50
+ "spec/data/icon@2x.png",
51
+ "spec/data/logo.png",
52
+ "spec/data/logo@2x.png",
53
+ "spec/lib/commands/build_spec.rb",
54
+ "spec/lib/commands/commands_spec.rb",
55
+ "spec/lib/commands/commands_spec_helper.rb",
56
+ "spec/lib/commands/generate_spec.rb",
57
+ "spec/lib/passbook/pkpass_spec.rb",
58
+ "spec/lib/passbook/push_notification_spec.rb",
59
+ "spec/lib/passbook/signer_spec.rb",
60
+ "spec/lib/rack/passbook_rack_spec.rb",
61
+ "spec/spec_helper.rb"
62
+ ]
63
+ s.homepage = "https://github.com/pgharts/passbookpgh"
64
+ s.licenses = ["MIT"]
65
+ s.rubygems_version = "2.4.8"
66
+ s.summary = "A IOS Passbook generator."
67
+
68
+ if s.respond_to? :specification_version then
69
+ s.specification_version = 4
70
+
71
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
72
+ s.add_runtime_dependency(%q<rubyzip>, [">= 1.0.0"])
73
+ s.add_runtime_dependency(%q<grocer>, [">= 0"])
74
+ s.add_runtime_dependency(%q<commander>, [">= 0"])
75
+ s.add_runtime_dependency(%q<terminal-table>, [">= 0"])
76
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
77
+ s.add_development_dependency(%q<activesupport>, [">= 0"])
78
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
79
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
80
+ s.add_development_dependency(%q<rspec>, [">= 0"])
81
+ s.add_development_dependency(%q<rake>, [">= 0"])
82
+ s.add_development_dependency(%q<yard>, [">= 0"])
83
+ else
84
+ s.add_dependency(%q<rubyzip>, [">= 1.0.0"])
85
+ s.add_dependency(%q<grocer>, [">= 0"])
86
+ s.add_dependency(%q<commander>, [">= 0"])
87
+ s.add_dependency(%q<terminal-table>, [">= 0"])
88
+ s.add_dependency(%q<rack-test>, [">= 0"])
89
+ s.add_dependency(%q<activesupport>, [">= 0"])
90
+ s.add_dependency(%q<jeweler>, [">= 0"])
91
+ s.add_dependency(%q<simplecov>, [">= 0"])
92
+ s.add_dependency(%q<rspec>, [">= 0"])
93
+ s.add_dependency(%q<rake>, [">= 0"])
94
+ s.add_dependency(%q<yard>, [">= 0"])
95
+ end
96
+ else
97
+ s.add_dependency(%q<rubyzip>, [">= 1.0.0"])
98
+ s.add_dependency(%q<grocer>, [">= 0"])
99
+ s.add_dependency(%q<commander>, [">= 0"])
100
+ s.add_dependency(%q<terminal-table>, [">= 0"])
101
+ s.add_dependency(%q<rack-test>, [">= 0"])
102
+ s.add_dependency(%q<activesupport>, [">= 0"])
103
+ s.add_dependency(%q<jeweler>, [">= 0"])
104
+ s.add_dependency(%q<simplecov>, [">= 0"])
105
+ s.add_dependency(%q<rspec>, [">= 0"])
106
+ s.add_dependency(%q<rake>, [">= 0"])
107
+ s.add_dependency(%q<yard>, [">= 0"])
108
+ end
109
+ end
110
+
Binary file
Binary file
Binary file
Binary file