passbookpgh 0.4.5

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.
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