Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support For One Object, Multiple Templates #18

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,38 @@ your password in config/initializers/passbook_#{model_name}.rb.
and go to \passes\model_name on your iphone (make sure it is in debug mode and allows http connections)


### Supporting One Object, Many Pass Templates
If you wish to have an object that supports many pass templates, then your object
must have #pass_type_id. That way when you attempt to render the object, the value
of pass_type_id will be matched amongst your pass templates defined in your config.
The matched template will be used.

For example, say you have a pass type id and a cert from Apple. You want to use that
but want to distribute passes with a number of looks. Define your templates with the
certificate path and password, and the path to the template. Then have your object
return the correct value for #pass_type_id.

```
# Use the same Apple Pass Type ID for all of our passes.
# Define custom looks.
Passbook::Config.instance.add_pkpass do |passbook|
shared_pass_data = {
"cert_path" => "path/to/desired/cert.p12",
"cert_password" => "password_for_cert"
}

passbook.pass_config["my_template_1"] = shared_pass_data.merge("template_path" => "path/to/desired/template1")
passbook.pass_config["my_template_2"] = shared_pass_data.merge("template_path" => "path/to/desired/template2")
end
```

```
# Render template 1 for special situations.
my_object.pass_type_id = my_object.is_special? "my_template_1" : "my_template_2"
render :pkpass, my_object
```



#### Check out [FAQs] wiki section if you get in trouble

Expand Down
2 changes: 1 addition & 1 deletion app/models/passbook/registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module Passbook
#
# @private
class Registration < ActiveRecord::Base
set_table_name "passbook_registrations"
self.table_name = "passbook_registrations"
attr_accessible :device_id, :pass_type_id, :push_token, :serial_number, :uuid
end
end
32 changes: 27 additions & 5 deletions lib/passbook-ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,35 @@ def self.pass_type_id_to_class pass_type_id
Passbook::Config.instance.pass_config[pass_type_id]['class'].constantize
end

# @private
def self.class_name_to_pass_type_id class_name
Passbook::Config.instance.pass_config.each do |pass_type_id, config|
return pass_type_id if config['class']==class_name
# Public: Determine if the given object matches the specified config.
#
# obj - Object to determine what config to use.
# pass_type_id - String ID of the pass defined in the initializer config.
# pass_config - Config to match against.
#
# Returns true if the object represents the pass in the config.
def self.object_matches_pass_config(obj, pass_type_id, pass_config)
if obj.respond_to? :pass_type_id
# Allow 1 class type to represent any pass (good for multiple pass templates)
obj.pass_type_id == pass_type_id
else
# Fallback. Bind a class to a pass by type. This will only match the first
# found in the case of multiple pass definitions. This exists for
# backwards compatibility.
obj.class.to_s == pass_config["class"]
end
end

# Public: Determine what pass to use for the specified object.
# Returns a string id for the pass configuration.
def self.find_pass_type_id_for(obj)
Passbook::Config.instance.pass_config.each do |pass_id, pass_config|
return pass_id if object_matches_pass_config obj, pass_id, pass_config
end

nil
end

# Registers a custom renderer for .pkpass files
# (executed at load time)
#
Expand All @@ -47,7 +69,7 @@ def self.register_pkpass
Mime::Type.register 'application/vnd.apple.pkpass', :pkpass

ActionController::Renderers.add :pkpass do |obj, options|
pkpass = Passbook::Pkpass.new Passbook.class_name_to_pass_type_id(obj.class.to_s), obj.serial_number
pkpass = Passbook::Pkpass.new Passbook.find_pass_type_id_for(obj), obj.serial_number
obj.update_pass pkpass if obj.respond_to? :update_pass
pkpass_io = pkpass.package
response.headers["last-modified"] = obj.updated_at.strftime("%Y-%m-%d %H:%M:%S")
Expand Down
97 changes: 97 additions & 0 deletions spec/passbook_ruby_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require "spec_helper"

describe Passbook do

class DummyPass; end

let(:dummy) { DummyPass.new }

context "#object_matches_pass_config:" do

context "when object responds to :pass_type_id" do

before(:each) { DummyPass.any_instance.stub(:pass_type_id).and_return "test_pass" }

it "returns true when object.pass_type_id == specified pass_type_id" do
expect(Passbook.object_matches_pass_config dummy, "test_pass", {}).to be_true
end

it "returns false when object is no match" do
expect(Passbook.object_matches_pass_config dummy, "new_pass", {}).to be_false
end

end

context "when object doesn't respond to :pass_type_id" do

let(:pass_config) { { "class" => "DummyPass" } }

it "returns true when object class name == class name in pass config" do
expect(Passbook.object_matches_pass_config dummy, "random_pass", pass_config).to be_true
end

it "returns false when object is no match" do
pass_config["class"] = "NotAMatch"
expect(Passbook.object_matches_pass_config dummy, "random_pass", pass_config).to be_false
end

end

end

context "#find_pass_type_id_for:" do

before do
Passbook::Config.instance.stub(:pass_config).and_return(
"test_pass1" => {
"cert_path" => "some/random/path1",
"cert_password" => SecureRandom.hex(10),
"template_path" => "another/random/path1",
"class" => "DummyPass"
},
"test_pass2" => {
"cert_path" => "some/random/path2",
"cert_password" => SecureRandom.hex(10),
"template_path" => "another/random/path3",
"class" => "DummyPass"
},
"test_pass3" => {
"cert_path" => "some/random/path3",
"cert_password" => SecureRandom.hex(10),
"template_path" => "another/random/path3",
"class" => "DummyPass"
}
)
end

context "when object responds to :pass_type_id" do

it "returns a String when object.pass_type_id == specified pass_type_id" do
DummyPass.any_instance.stub(:pass_type_id).and_return "test_pass2"
expect(Passbook.find_pass_type_id_for dummy).to eq("test_pass2")
end

it "returns Nil when object is no match" do
DummyPass.any_instance.stub(:pass_type_id).and_return "test_pass9"
expect(Passbook.find_pass_type_id_for dummy).to be_nil
end

end

context "when object doesn't respond to :pass_type_id" do

it "returns a String when object class name == class name in pass config" do
expect(Passbook.find_pass_type_id_for dummy).to eq("test_pass1")
end

it "returns Nil when object is no match" do
dummy.stub_chain(:class, :to_s).and_return "FakeName"
expect(Passbook.find_pass_type_id_for dummy).to be_nil
end

end

end


end