├── log
└── .gitkeep
├── .rspec
├── lib
├── tasks
│ └── .gitkeep
├── assets
│ └── .gitkeep
├── templates
│ └── haml
│ │ └── scaffold
│ │ └── _form.html.haml
└── rails_ext
│ └── tag_helper_ext.rb
├── public
├── favicon.ico
├── assets
│ ├── rails-2da85d4b70342e6b8b7ec929d911af9f.png
│ ├── application-6e3658cb51743044eed0ffab7c61d512.js.gz
│ ├── application-d8aebfd478eee5c4f8dbe8411bcde500.css.gz
│ ├── fontawesome-webfont-658b64bfa55eff9dcd175bfd3ac3238a.eot
│ ├── fontawesome-webfont-98d61732d4d0af2312cfc7c1d005594b.woff
│ ├── fontawesome-webfont-efc9e487a6c573ca65c0d1a10c09f63d.ttf
│ ├── twitter
│ │ └── bootstrap
│ │ │ ├── glyphicons-halflings-d83dfe9df6cd3f50b5ae69abe919e065.png
│ │ │ └── glyphicons-halflings-white-e06c893995f68ff48aa1b2e591f27889.png
│ └── manifest-500885f534fb666445deb60092afb357.json
├── robots.txt
├── 500.html
├── 422.html
└── 404.html
├── app
├── mailers
│ └── .gitkeep
├── models
│ ├── .gitkeep
│ ├── history_event.rb
│ └── user.rb
├── helpers
│ ├── users_helper.rb
│ ├── customers_helper.rb
│ ├── history_events_helper.rb
│ ├── credit_cards_helper.rb
│ ├── application_helper.rb
│ ├── subscriptions_helper.rb
│ ├── addresses_helper.rb
│ └── transactions_helper.rb
├── assets
│ ├── images
│ │ └── rails.png
│ ├── javascripts
│ │ ├── bootstrap.js.coffee
│ │ ├── bootstrap.js
│ │ ├── customers.js.coffee
│ │ ├── users.js.coffee
│ │ ├── history_events.js.coffee
│ │ ├── application.js
│ │ ├── addresses.js.coffee
│ │ ├── transactions.js.coffee
│ │ ├── credit_cards.js.coffee
│ │ ├── braintree-data.js
│ │ └── braintree.js
│ └── stylesheets
│ │ ├── users.css.less
│ │ ├── addresses.css.less
│ │ ├── customers.css.less
│ │ ├── credit_cards.css.less
│ │ ├── history_events.css.less
│ │ ├── transactions.css.less
│ │ ├── application.css
│ │ ├── scaffolds.css.less
│ │ └── bootstrap_and_overrides.css.less
├── controllers
│ ├── application_controller.rb
│ ├── history_events_controller.rb
│ ├── plans_controller.rb
│ ├── add_ons_controller.rb
│ ├── discounts_controller.rb
│ ├── users_controller.rb
│ ├── customers_controller.rb
│ ├── addresses_controller.rb
│ ├── credit_cards_controller.rb
│ ├── transactions_controller.rb
│ └── subscriptions_controller.rb
└── views
│ ├── users
│ ├── new.html.haml
│ ├── edit.html.haml
│ ├── _form.html.haml
│ ├── show.html.haml
│ └── index.html.haml
│ ├── customers
│ ├── new.html.haml
│ ├── edit.html.haml
│ ├── _form.html.haml
│ └── show.html.haml
│ ├── subscriptions
│ ├── new.html.haml
│ ├── edit.html.haml
│ ├── show.html.haml
│ ├── _form.html.haml
│ └── index.html.haml
│ ├── addresses
│ ├── new.html.haml
│ ├── edit.html.haml
│ ├── _form.html.haml
│ ├── show.html.haml
│ └── index.html.haml
│ ├── credit_cards
│ ├── new.html.haml
│ ├── edit.html.haml
│ ├── index.html.haml
│ ├── show.html.haml
│ └── _form.html.haml
│ ├── add_ons
│ ├── show.html.haml
│ └── index.html.haml
│ ├── discounts
│ ├── show.html.haml
│ └── index.html.haml
│ ├── plans
│ ├── show.html.haml
│ └── index.html.haml
│ ├── transactions
│ ├── new.html.haml
│ ├── show.html.haml
│ ├── index.html.haml
│ └── _form.html.haml
│ ├── history_events
│ └── index.html.haml
│ └── layouts
│ └── application.html.haml
├── vendor
├── plugins
│ └── .gitkeep
└── assets
│ ├── javascripts
│ └── .gitkeep
│ └── stylesheets
│ └── .gitkeep
├── bin
├── rake
├── bundle
└── rails
├── config
├── initializers
│ ├── custom_validations.rb
│ ├── session_store.rb
│ ├── mime_types.rb
│ ├── filter_parameter_logging.rb
│ ├── custom_callbacks.rb
│ ├── backtrace_silencers.rb
│ ├── braintree.rb
│ ├── wrap_parameters.rb
│ ├── inflections.rb
│ ├── secret_token.rb
│ └── simple_form.rb
├── boot.rb
├── environment.rb
├── database.yml
├── locales
│ ├── en.bootstrap.yml
│ ├── simple_form.en.yml
│ └── en.yml
├── routes.rb
├── application.rb
├── environments
│ ├── development.rb
│ ├── test.rb
│ └── production.rb
└── newrelic.yml
├── config.ru
├── doc
└── README_FOR_APP
├── db
├── migrate
│ ├── 20120914061844_create_users.rb
│ └── 20130322050449_create_history_events.rb
├── seeds.rb
└── schema.rb
├── spec
├── support
│ ├── capybara.rb
│ ├── braintree_integration
│ │ ├── braintree_auth.yml.example
│ │ ├── utils.rb
│ │ ├── configuration.rb
│ │ ├── test_data.rb
│ │ └── setup.rb
│ ├── ruby_ext.rb
│ └── braintree_integration.rb
├── features
│ ├── plans_management_spec.rb
│ ├── user_management_spec.rb
│ ├── customer_management_spec.rb
│ └── address_management_spec.rb
├── models
│ └── user_spec.rb
└── spec_helper.rb
├── Rakefile
├── script
└── rails
├── .gitignore
├── Gemfile
├── MIT-LICENSE
├── README.md
├── Gemfile.lock
└── README.rdoc
/log/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 |
--------------------------------------------------------------------------------
/lib/tasks/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/plugins/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/helpers/users_helper.rb:
--------------------------------------------------------------------------------
1 | module UsersHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/customers_helper.rb:
--------------------------------------------------------------------------------
1 | module CustomersHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/history_events_helper.rb:
--------------------------------------------------------------------------------
1 | module HistoryEventsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/app/assets/images/rails.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/app/assets/images/rails.png
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery
3 | end
4 |
--------------------------------------------------------------------------------
/app/views/users/new.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = User
2 | .page-header
3 | %h1="New #{model_class.model_name.human}"
4 | = render partial: "form"
5 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bootstrap.js.coffee:
--------------------------------------------------------------------------------
1 | jQuery ->
2 | $("a[rel=popover]").popover()
3 | $(".tooltip").tooltip()
4 | $("a[rel=tooltip]").tooltip()
--------------------------------------------------------------------------------
/app/views/users/edit.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = User
2 | .page-header
3 | %h1="Edit #{model_class.model_name.human}"
4 | = render partial: "form"
5 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | jQuery(function() {
2 | $("a[rel~=popover], .has-popover").popover();
3 | $("a[rel~=tooltip], .has-tooltip").tooltip();
4 | });
5 |
--------------------------------------------------------------------------------
/app/views/customers/new.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Customer
2 | .page-header
3 | %h1="New #{model_class.model_name.human}"
4 | = render partial: "form"
5 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../../config/application', __FILE__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/app/views/customers/edit.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Customer
2 | .page-header
3 | %h1="Edit #{model_class.model_name.human}"
4 | = render partial: "form"
5 |
--------------------------------------------------------------------------------
/public/assets/rails-2da85d4b70342e6b8b7ec929d911af9f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/rails-2da85d4b70342e6b8b7ec929d911af9f.png
--------------------------------------------------------------------------------
/app/controllers/history_events_controller.rb:
--------------------------------------------------------------------------------
1 | class HistoryEventsController < ApplicationController
2 | def index
3 | @events = HistoryEvent.order('created_at desc')
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/config/initializers/custom_validations.rb:
--------------------------------------------------------------------------------
1 | BraintreeRails::BillingAddressValidator.setup do |validations|
2 | validations << [:country_code_alpha2, inclusion: { in: ["CA", "US"] }]
3 | end
4 |
--------------------------------------------------------------------------------
/public/assets/application-6e3658cb51743044eed0ffab7c61d512.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/application-6e3658cb51743044eed0ffab7c61d512.js.gz
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run BraintreeRailsExample::Application
5 |
--------------------------------------------------------------------------------
/public/assets/application-d8aebfd478eee5c4f8dbe8411bcde500.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/application-d8aebfd478eee5c4f8dbe8411bcde500.css.gz
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 |
4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5 |
--------------------------------------------------------------------------------
/public/assets/fontawesome-webfont-658b64bfa55eff9dcd175bfd3ac3238a.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/fontawesome-webfont-658b64bfa55eff9dcd175bfd3ac3238a.eot
--------------------------------------------------------------------------------
/public/assets/fontawesome-webfont-98d61732d4d0af2312cfc7c1d005594b.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/fontawesome-webfont-98d61732d4d0af2312cfc7c1d005594b.woff
--------------------------------------------------------------------------------
/public/assets/fontawesome-webfont-efc9e487a6c573ca65c0d1a10c09f63d.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/fontawesome-webfont-efc9e487a6c573ca65c0d1a10c09f63d.ttf
--------------------------------------------------------------------------------
/app/views/subscriptions/new.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Subscription
2 | .page-header
3 | %h1="New #{model_class.model_name.human}"
4 | = render partial: "form", locals: {url: subscriptions_path}
5 |
--------------------------------------------------------------------------------
/app/views/addresses/new.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Address
2 | .page-header
3 | %h1="New #{model_class.model_name.human}"
4 | = render partial: "form", locals: {url: user_customer_addresses_path(@user)}
5 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | BraintreeRailsExample::Application.config.session_store :cookie_store, key: '_braintree_rails_example_session'
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/users.css.less:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Users controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require File.expand_path('../application', __FILE__)
3 | require 'pp'
4 |
5 | # Initialize the Rails application.
6 | BraintreeRailsExample::Application.initialize!
7 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/addresses.css.less:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Addresses controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/customers.css.less:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Customers controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/views/addresses/edit.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Address
2 | .page-header
3 | %h1="Edit #{model_class.model_name.human}"
4 | = render partial: "form", locals: {url: user_customer_address_path(@user, @address)}
5 |
--------------------------------------------------------------------------------
/app/views/credit_cards/new.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::CreditCard
2 | .page-header
3 | %h1="New #{model_class.model_name.human}"
4 | = render partial: "form", locals: {url: user_customer_credit_cards_path(@user)}
5 |
--------------------------------------------------------------------------------
/app/views/subscriptions/edit.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Subscription
2 | .page-header
3 | %h1="Edit #{model_class.model_name.human}"
4 | = render partial: "form", locals: {url: subscription_path(@subscription)}
5 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/credit_cards.css.less:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the CreditCards controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/doc/README_FOR_APP:
--------------------------------------------------------------------------------
1 | Use this README file to introduce your application and point to useful places in the API for learning more.
2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
3 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/history_events.css.less:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the history_events controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/transactions.css.less:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Transactions controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/models/history_event.rb:
--------------------------------------------------------------------------------
1 | class HistoryEvent < ActiveRecord::Base
2 | serialize :data
3 | def self.create_from(braintree_object)
4 | create(model: braintree_object.class.name, data: braintree_object.attributes)
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/public/assets/twitter/bootstrap/glyphicons-halflings-d83dfe9df6cd3f50b5ae69abe919e065.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/twitter/bootstrap/glyphicons-halflings-d83dfe9df6cd3f50b5ae69abe919e065.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-Agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/app/views/credit_cards/edit.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::CreditCard
2 | .page-header
3 | %h1="Edit #{model_class.model_name.human}"
4 | = render partial: "form", locals: {url: user_customer_credit_card_path(@user, @credit_card)}
5 |
--------------------------------------------------------------------------------
/app/views/users/_form.html.haml:
--------------------------------------------------------------------------------
1 | = simple_form_for @user, html: { class: 'form-horizontal' } do |f|
2 | = f.input :email
3 | .form-actions
4 | = f.button :submit, class: 'btn-primary'
5 | = link_to 'Cancel', users_path, class: 'btn'
6 |
--------------------------------------------------------------------------------
/app/controllers/plans_controller.rb:
--------------------------------------------------------------------------------
1 | class PlansController < ApplicationController
2 | def index
3 | @plans = BraintreeRails::Plan.all
4 | end
5 |
6 | def show
7 | @plan = BraintreeRails::Plan.find(params[:id])
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 | # Mime::Type.register_alias "text/html", :iphone
6 |
--------------------------------------------------------------------------------
/public/assets/twitter/bootstrap/glyphicons-halflings-white-e06c893995f68ff48aa1b2e591f27889.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyang/braintree-rails-example/HEAD/public/assets/twitter/bootstrap/glyphicons-halflings-white-e06c893995f68ff48aa1b2e591f27889.png
--------------------------------------------------------------------------------
/app/controllers/add_ons_controller.rb:
--------------------------------------------------------------------------------
1 | class AddOnsController < ApplicationController
2 | def index
3 | @add_ons = BraintreeRails::AddOn.all
4 | end
5 |
6 | def show
7 | @add_on = BraintreeRails::AddOn.find(params[:id])
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20120914061844_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration
2 | def change
3 | create_table :users do |t|
4 | t.string :email
5 | t.string :customer_id
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password, :number, :cvv]
5 |
--------------------------------------------------------------------------------
/app/assets/javascripts/customers.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/users.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
4 |
--------------------------------------------------------------------------------
/app/controllers/discounts_controller.rb:
--------------------------------------------------------------------------------
1 | class DiscountsController < ApplicationController
2 | def index
3 | @discounts = BraintreeRails::Discount.all
4 | end
5 |
6 | def show
7 | @discount = BraintreeRails::Discount.find(params[:id])
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/assets/javascripts/history_events.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
4 |
--------------------------------------------------------------------------------
/config/initializers/custom_callbacks.rb:
--------------------------------------------------------------------------------
1 | [User, BraintreeRails::Address, BraintreeRails::Customer, BraintreeRails::CreditCard, BraintreeRails::Transaction, BraintreeRails::Subscription].each do |model|
2 | model.after_create do
3 | HistoryEvent.create_from(self)
4 | end
5 | end
--------------------------------------------------------------------------------
/db/migrate/20130322050449_create_history_events.rb:
--------------------------------------------------------------------------------
1 | class CreateHistoryEvents < ActiveRecord::Migration
2 | def change
3 | create_table :history_events do |t|
4 | t.string :model
5 | t.text :data
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/support/capybara.rb:
--------------------------------------------------------------------------------
1 | require 'capybara/rails'
2 |
3 | module CapybaraHelper
4 | def fill_in_all(namespace, attributes)
5 | attributes.to_form_params(namespace).each do |attribute, value|
6 | find_field(attribute).set(value)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/support/braintree_integration/braintree_auth.yml.example:
--------------------------------------------------------------------------------
1 | ---
2 | merchant_id: 'merchant_id'
3 | public_key: 'public_key'
4 | private_key: 'private_key'
5 | client_side_encryption_key: 'client_side_encryption_key'
6 | login: 'braintree-sandbox-username'
7 | password: 'password'
8 |
9 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | # Add your own tasks in files placed in lib/tasks ending in .rake,
3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4 |
5 | require File.expand_path('../config/application', __FILE__)
6 |
7 | BraintreeRailsExample::Application.load_tasks
8 |
--------------------------------------------------------------------------------
/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/spec/support/braintree_integration/utils.rb:
--------------------------------------------------------------------------------
1 | module BraintreeIntegration
2 | module Utils
3 | def create_customer_for(user, attributes = customer_hash)
4 | BraintreeRails::Customer.create!(attributes).tap do |customer|
5 | user.update_attribute(:customer_id, customer.id)
6 | end
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/templates/haml/scaffold/_form.html.haml:
--------------------------------------------------------------------------------
1 | = simple_form_for(@<%= singular_table_name %>) do |f|
2 | = f.error_notification
3 |
4 | .form-inputs
5 | <%- attributes.each do |attribute| -%>
6 | = f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %>
7 | <%- end -%>
8 |
9 | .form-actions
10 | = f.button :submit
11 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: sqlite3
3 | database: db/development.sqlite3
4 | pool: 5
5 | timeout: 5000
6 |
7 | test:
8 | adapter: sqlite3
9 | database: db/test.sqlite3
10 | pool: 5
11 | timeout: 5000
12 |
13 | production:
14 | adapter: sqlite3
15 | database: db/production.sqlite3
16 | pool: 5
17 | timeout: 5000
18 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ActiveRecord::Base
2 | validates :email, uniqueness: true, presence: true
3 | after_destroy :destroy_customer
4 |
5 | def customer
6 | customer_id && BraintreeRails::Customer.new(customer_id)
7 | end
8 |
9 | private
10 | def destroy_customer
11 | BraintreeRails::Customer.delete(customer_id) if customer_id.present?
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 | User.create(email:'braintree+rails@example.com')
9 |
--------------------------------------------------------------------------------
/app/helpers/credit_cards_helper.rb:
--------------------------------------------------------------------------------
1 | module CreditCardsHelper
2 | def options_for_month_select
3 | {
4 | collection: Date::MONTHNAMES.each_with_index.to_a[1..-1].map { |month, index| [month, index.to_s.rjust(2, '0')]},
5 | include_blank: false,
6 | }
7 | end
8 |
9 | def options_for_year_select
10 | {
11 | collection: 1976..2200,
12 | include_blank: false,
13 | }
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/add_ons/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::AddOn
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - @add_on.attributes.each do |attribute, value|
5 | - next unless value.present?
6 | %p
7 | %strong= model_class.human_attribute_name(attribute) + ':'
8 | %br
9 | = @add_on.send(attribute)
10 |
11 | .form-actions
12 | = link_to 'Back', add_ons_path, class: 'btn'
13 |
--------------------------------------------------------------------------------
/app/views/discounts/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Discount
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - @discount.attributes.each do |attribute, value|
5 | - next unless value.present?
6 | %p
7 | %strong= model_class.human_attribute_name(attribute) + ':'
8 | %br
9 | = @discount.send(attribute)
10 |
11 | .form-actions
12 | = link_to 'Back', discounts_path, class: 'btn'
13 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/lib/rails_ext/tag_helper_ext.rb:
--------------------------------------------------------------------------------
1 | module ActionView
2 | module Helpers
3 | module TagHelper
4 | def tag_options_with_data_encrypted_name(options, escape = true)
5 | if options['data-encrypted-name']
6 | options['data-encrypted-name'] = options.delete("name")
7 | end
8 | tag_options_without_data_encrypted_name(options, escape)
9 | end
10 | alias_method_chain :tag_options, :data_encrypted_name
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/config/initializers/braintree.rb:
--------------------------------------------------------------------------------
1 | require 'rails_ext/tag_helper_ext'
2 | BraintreeRails::Configuration.environment = :sandbox
3 | BraintreeRails::Configuration.logger = Logger.new('log/braintree.log')
4 | BraintreeRails::Configuration.merchant_id = ENV['MERCHANT_ID']
5 | BraintreeRails::Configuration.public_key = ENV['PUBLIC_KEY']
6 | BraintreeRails::Configuration.private_key = ENV['PRIVATE_KEY']
7 | BraintreeRails::Configuration.client_side_encryption_key = ENV['CLIENT_SIDE_ENCRYPTION_KEY']
8 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | def bootstrap_flash
3 | end
4 |
5 | def options_for_credit_card_select(customer)
6 | {
7 | collection: customer.credit_cards,
8 | label_method: :masked_number,
9 | include_blank: false,
10 | }
11 | end
12 |
13 | def options_for_plan_select
14 | {
15 | collection: BraintreeRails::Plan.all,
16 | label_method: :id,
17 | include_blank: false,
18 | }
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/helpers/subscriptions_helper.rb:
--------------------------------------------------------------------------------
1 | module SubscriptionsHelper
2 |
3 | def new_subscription_path
4 | path ||= new_user_customer_credit_card_subscription_path(@user, @credit_card) if @credit_card
5 | path ||= new_plan_subscription_path(@plan) if @plan
6 | path ||= super
7 | end
8 |
9 | def back_path
10 | path ||= user_customer_credit_card_path(@user, @credit_card) if @credit_card
11 | path ||= plan_path(@plan) if @plan
12 | path ||= users_path
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/views/plans/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Plan
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - @plan.attributes.each do |attribute, value|
5 | - next unless value.present?
6 | %p
7 | %strong= model_class.human_attribute_name(attribute) + ':'
8 | %br
9 | = @plan.send(attribute)
10 |
11 | .form-actions
12 | = link_to 'Back', plans_path, class: 'btn'
13 | = link_to 'Subscriptions', plan_subscriptions_path(@plan), class: 'btn'
14 |
--------------------------------------------------------------------------------
/app/views/transactions/new.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Transaction
2 | .page-header
3 | %h1="New #{model_class.model_name.human}"
4 | = render partial: "form"
5 |
6 | - content_for :javascript do
7 | :javascript
8 | var braintree = Braintree.create("#{BraintreeRails::Configuration.client_side_encryption_key}")
9 | braintree.onSubmitEncryptForm('braintree-transaction-form');
10 | BraintreeData.setup("#{BraintreeRails::Configuration.merchant_id}", "braintree-transaction-form", BraintreeData.environments.sandbox)
11 |
--------------------------------------------------------------------------------
/spec/support/ruby_ext.rb:
--------------------------------------------------------------------------------
1 | class Object
2 | def to_form_params(namespace)
3 | {namespace => to_s}
4 | end
5 | end
6 |
7 | class Array
8 | def to_form_params(namespace)
9 | inject({}) do |params, value|
10 | params.merge(value.to_form_params("#{namespace}[]"))
11 | end
12 | end
13 | end
14 |
15 | class Hash
16 | def to_form_params(namespace = nil)
17 | inject({}) do |params, pair|
18 | params.merge(pair.last.to_form_params(namespace ? "#{namespace}[#{pair.first}]" : pair.first))
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/views/users/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = User
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 |
5 | %p
6 | %strong= model_class.human_attribute_name(:email) + ':'
7 | %br
8 | = @user.email
9 |
10 | .form-actions
11 | = link_to 'Back', users_path, class: 'btn'
12 | = link_to 'Edit', edit_user_path(@user), class: 'btn'
13 | = link_to 'Customer info', user_customer_path(@user), class: 'btn'
14 | = link_to 'Destroy', user_path(@user), method: 'delete', data: {confirm: 'Are you sure?'}, class: 'btn btn-danger'
15 |
--------------------------------------------------------------------------------
/app/views/customers/_form.html.haml:
--------------------------------------------------------------------------------
1 | = simple_form_for @customer, url: user_customer_path(@user), html: { class: 'form-horizontal' } do |f|
2 | - [:first_name, :last_name, :email, :company, :website, :phone, :fax].each do |attribute|
3 | = f.input attribute
4 | .form-actions
5 | = f.button :submit, class: 'btn-primary'
6 | = link_to 'Cancel', user_path(@user), class: 'btn'
7 |
8 | - content_for :javascript do
9 | :javascript
10 | BraintreeData.setup("#{BraintreeRails::Configuration.merchant_id}", "braintree-transaction-form", BraintreeData.environments.sandbox)
11 |
--------------------------------------------------------------------------------
/app/views/plans/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Plan
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:id)
8 | %th= model_class.human_attribute_name(:name)
9 | %th= model_class.human_attribute_name(:price)
10 | %tbody
11 | - @plans.each do |plan|
12 | %tr
13 | %td= link_to plan.id, plan_path(plan)
14 | %td= plan.name
15 | %td= plan.price
16 | = link_to 'Back', root_path, class: 'btn'
17 |
--------------------------------------------------------------------------------
/app/views/addresses/_form.html.haml:
--------------------------------------------------------------------------------
1 | = simple_form_for @address, url: url, html: { class: 'form-horizontal' } do |f|
2 | = f.input :first_name
3 | = f.input :last_name
4 | = f.input :company
5 | = f.input :street_address
6 | = f.input :extended_address
7 | = f.input :locality, label: 'City'
8 | = f.input :country_code_alpha2, options_for_country_select
9 | = f.input :region, options_for_region_select
10 | = f.input :postal_code
11 | .form-actions
12 | = f.button :submit, class: 'btn-primary'
13 | = link_to 'Cancel', user_customer_addresses_path(@user), class: 'btn'
14 |
--------------------------------------------------------------------------------
/config/locales/en.bootstrap.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | helpers:
6 | actions: "Actions"
7 | links:
8 | back: "Back"
9 | cancel: "Cancel"
10 | confirm: "Are you sure?"
11 | destroy: "Delete"
12 | new: "New"
13 | edit: "Edit"
14 | titles:
15 | edit: "Edit %{model}"
16 | save: "Save %{model}"
17 | new: "New %{model}"
18 | delete: "Delete %{model}"
19 |
--------------------------------------------------------------------------------
/app/views/add_ons/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::AddOn
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:id)
8 | %th= model_class.human_attribute_name(:name)
9 | %th= model_class.human_attribute_name(:amount)
10 | %tbody
11 | - @add_ons.each do |add_on|
12 | %tr
13 | %td= link_to add_on.id, add_on_path(add_on)
14 | %td= add_on.name
15 | %td= add_on.amount
16 | = link_to 'Back', root_path, class: 'btn'
17 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile ~/.gitignore_global
6 |
7 | # Ignore bundler config
8 | /.bundle
9 |
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 |
13 | # Ignore all logfiles and tempfiles.
14 | /log/*.log
15 | /tmp
16 | .rvmrc
17 | .ruby-version
18 | .ruby-gemset
19 | tags
20 | /spec/support/braintree_integration/braintree_auth.yml
21 |
--------------------------------------------------------------------------------
/app/views/discounts/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Discount
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:id)
8 | %th= model_class.human_attribute_name(:name)
9 | %th= model_class.human_attribute_name(:amount)
10 | %tbody
11 | - @discounts.each do |discount|
12 | %tr
13 | %td= link_to discount.id, discount_path(discount)
14 | %td= discount.name
15 | %td= discount.amount
16 | = link_to 'Back', root_path, class: 'btn'
17 |
--------------------------------------------------------------------------------
/app/views/transactions/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Transaction
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - [:id, :amount, :status].each do |attribute|
5 | %p
6 | %strong= model_class.human_attribute_name(attribute) + ':'
7 | %br
8 | = @transaction.send(attribute)
9 | %p
10 | %strong= model_class.human_attribute_name(:credit_card) + ':'
11 | %br
12 | = @transaction.credit_card.masked_number
13 | %p
14 | %strong= model_class.human_attribute_name(:customer) + ':'
15 | %br
16 | = @transaction.customer.full_name
17 |
18 | .form-actions
19 | = link_to 'Back', transactions_path, class: 'btn'
20 |
--------------------------------------------------------------------------------
/spec/features/plans_management_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Address Management", braintree_integration: true do
4 | it "shows a list of plans" do
5 | address = @user.customer.addresses.create!(address_hash)
6 | visit user_customer_addresses_path(@user)
7 | page.should have_link(address.street_address, href: user_customer_address_path(@user, address))
8 | end
9 |
10 | it "shows plan attributes" do
11 | address = @user.customer.addresses.create!(address_hash)
12 | visit user_customer_address_path(@user, address)
13 | address_hash.values.each do |value|
14 | page.should have_content(value)
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/support/braintree_integration.rb:
--------------------------------------------------------------------------------
1 | require_relative 'braintree_integration/configuration'
2 | require_relative 'braintree_integration/setup'
3 | require_relative 'braintree_integration/test_data'
4 | require_relative 'braintree_integration/utils'
5 |
6 | BraintreeRails::Configuration.environment = :sandbox
7 | BraintreeRails::Configuration.logger = Logger.new('/dev/null').tap { |logger| logger.level = Logger::INFO }
8 |
9 | module BraintreeIntegration
10 | def self.included(receiver)
11 | receiver.send :include, Configuration
12 | receiver.send :include, Setup
13 | receiver.send :include, TestData
14 | receiver.send :include, Utils
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/views/history_events/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = HistoryEvent
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:id)
8 | %th= model_class.human_attribute_name(:model)
9 | %th= model_class.human_attribute_name(:created_at)
10 | %th= model_class.human_attribute_name(:data)
11 | %tbody
12 | - @events.each do |event|
13 | %tr
14 | %td= event.id
15 | %td= event.model
16 | %td= event.created_at
17 | %td
18 | %pre= PP.pp(event.data, "")
19 | = link_to 'Back', root_path, class: 'btn'
20 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/app/views/addresses/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Address
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - [:full_name, :company, :street_address, :extended_address, :locality, :region, :postal_code, :country_code_alpha2].each do |attribute|
5 | %p
6 | %strong= model_class.human_attribute_name(attribute) + ':'
7 | %br
8 | = @address.send(attribute)
9 |
10 | .form-actions
11 | = link_to 'Back', user_customer_addresses_path(@user), class: 'btn'
12 | = link_to 'Edit', edit_user_customer_address_path(@user, @address), class: 'btn'
13 | = link_to 'Destroy', user_customer_address_path(@user, @address), method: "delete", data: {confirm: 'Are you sure?'}, class: 'btn btn-danger'
14 |
--------------------------------------------------------------------------------
/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rake secret` to generate a secure secret key.
9 |
10 | # Make sure your secret_key_base is kept private
11 | # if you're sharing your code publicly.
12 | BraintreeRailsExample::Application.config.secret_key_base = '644bf3e66af41a6fd9e28ba87e291d51a0ddd4f57c1c514bf937b6ee030cbf0db4e77346a613f9c540f05a440384722e1f308175b917502c9d4a7decf5977a5b'
13 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
We're sorry, but something went wrong.
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // the compiled file.
9 | //
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 | // GO AFTER THE REQUIRES BELOW.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require twitter/bootstrap
16 | //= require_tree .
17 |
--------------------------------------------------------------------------------
/app/helpers/addresses_helper.rb:
--------------------------------------------------------------------------------
1 | module AddressesHelper
2 | def options_for_country_select
3 | {
4 | collection: available_countries,
5 | value_method: :alpha_2_code,
6 | label: 'Country',
7 | include_blank: false,
8 | }
9 | end
10 |
11 | def options_for_region_select
12 | {
13 | collection: available_countries,
14 | as: :grouped_select,
15 | group_method: :subregions,
16 | value_method: :name,
17 | }
18 | end
19 |
20 | private
21 | def available_countries
22 | braintree_country_alpha_2_codes = Braintree::Address::CountryNames.map {|country| country[1]}
23 | Carmen::Country.all.select { |country| braintree_country_alpha_2_codes.include?(country.alpha_2_code) }.sort_by(&:name)
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | BraintreeRailsExample::Application.routes.draw do
2 | resources :users do
3 | resource :customer do
4 | resources :credit_cards do
5 | resources :transactions, except: [:edit, :destroy]
6 | resources :subscriptions
7 | end
8 | resources :addresses
9 | resources :transactions, except: [:edit, :destroy]
10 | end
11 | end
12 | resources :transactions, except: [:edit, :destroy]
13 | resources :plans, only: [:index, :show] do
14 | resources :subscriptions
15 | end
16 | resources :add_ons, only: [:index, :show]
17 | resources :discounts, only: [:index, :show]
18 | resources :subscriptions, except: [:new, :create]
19 | resources :history_events, only: :index
20 | root to: 'users#index'
21 | end
22 |
--------------------------------------------------------------------------------
/app/helpers/transactions_helper.rb:
--------------------------------------------------------------------------------
1 | module TransactionsHelper
2 | def new_transaction_path
3 | path ||= new_user_customer_credit_card_transaction_path(@user, @credit_card) if @credit_card
4 | path ||= new_user_customer_transaction_path(@user) if @user
5 | path ||= super
6 | end
7 |
8 | def edit_subscription_path(subscription)
9 | path ||= edit_user_customer_credit_card_subscription_path(@user, @credit_card, subscription) if @credit_card
10 | path ||= edit_plan_subscription_path(@plan, subscription) if @plan
11 | path ||= super(subscription)
12 | end
13 |
14 | def back_path
15 | path ||= user_customer_credit_card_path(@user, @credit_card) if @credit_card
16 | path ||= user_customer_path(@user) if @user
17 | path ||= users_path
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | ruby "2.0.0"
3 |
4 | gem 'rails', '~> 4.0.3'
5 | gem 'simple_form'
6 | gem 'haml-rails'
7 | gem 'carmen-rails'
8 | gem 'thin'
9 | gem 'jquery-rails'
10 | gem 'braintree-rails', :github => "lyang/braintree-rails", :branch => 'master'
11 | gem 'coffee-rails', '~> 4.0.1'
12 | gem 'uglifier', '>= 2.5.0'
13 | gem 'therubyracer', :platforms => :ruby
14 | gem "less-rails"
15 | gem 'twitter-bootstrap-rails'
16 |
17 | group :development, :test do
18 | gem 'quiet_assets'
19 | gem 'sqlite3'
20 | gem 'rspec-rails', "~> 2.14.2"
21 | end
22 |
23 | group :test do
24 | gem 'capybara'
25 | gem "launchy"
26 | gem 'selenium-webdriver'
27 | gem 'shoulda-matchers'
28 | end
29 |
30 | group :production do
31 | gem 'newrelic_rpm'
32 | gem 'pg'
33 | gem 'rails_on_heroku'
34 | end
35 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The change you wanted was rejected.
23 |
Maybe you tried to change something you didn't have access to.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/config/locales/simple_form.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | simple_form:
3 | "yes": 'Yes'
4 | "no": 'No'
5 | required:
6 | text: 'required'
7 | mark: '*'
8 | # You can uncomment the line below if you need to overwrite the whole required html.
9 | # When using html, text and mark won't be used.
10 | # html: '*'
11 | error_notification:
12 | default_message: "Please review the problems below:"
13 | # Labels and hints examples
14 | # labels:
15 | # defaults:
16 | # password: 'Password'
17 | # user:
18 | # new:
19 | # email: 'E-mail to sign in.'
20 | # edit:
21 | # email: 'E-mail.'
22 | # hints:
23 | # defaults:
24 | # username: 'User name to sign in.'
25 | # password: 'No special characters, please.'
26 |
27 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require_self
12 | *= require_tree .
13 | *= require bootstrap_and_overrides
14 | */
15 |
16 | a.github {
17 | position: relative;
18 | float: right;
19 | }
20 |
21 | a.github img {
22 | position: absolute;
23 | top: -20px;
24 | right: -20px;
25 | border: 0;
26 | }
27 |
--------------------------------------------------------------------------------
/app/views/customers/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Customer
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - [:first_name, :last_name, :email, :company, :website, :phone, :fax].each do |attribute|
5 | %p
6 | %strong= model_class.human_attribute_name(attribute) + ':'
7 | %br
8 | = @customer.send(attribute)
9 |
10 | .form-actions
11 | = link_to 'Back', users_path, class: 'btn'
12 | = link_to 'Edit', edit_user_customer_path(@user), class: 'btn'
13 | = link_to 'Transactions', user_customer_transactions_path(@user), class: 'btn'
14 | = link_to 'Credit cards', user_customer_credit_cards_path(@user), class: 'btn'
15 | = link_to 'Addresses', user_customer_addresses_path(@user), class: 'btn'
16 | = link_to 'Destroy', user_customer_path(@user), method: "delete", data: {confirm: 'Are you sure?'}, class: 'btn btn-danger'
17 |
--------------------------------------------------------------------------------
/app/views/subscriptions/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Subscription
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - [:id, :price, :status, :balance].each do |attribute|
5 | %p
6 | %strong= model_class.human_attribute_name(attribute) + ':'
7 | %br
8 | = @subscription.send(attribute)
9 | %p
10 | %strong= model_class.human_attribute_name(:plan) + ':'
11 | %br
12 | = @subscription.plan.name
13 | %p
14 | %strong= BraintreeRails::CreditCard.human_attribute_name(:customer) + ':'
15 | %br
16 | = @subscription.credit_card.customer.full_name
17 |
18 | .form-actions
19 | = link_to 'Back', subscriptions_path, class: 'btn'
20 | = link_to 'Edit', edit_subscription_path(@subscription), class: 'btn'
21 | = link_to 'Destroy', subscription_path(@subscription), method: "delete", data: {confirm: 'Are you sure?'}, class: 'btn btn-danger'
22 |
--------------------------------------------------------------------------------
/app/views/users/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = User
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:id)
8 | %th= model_class.human_attribute_name(:email)
9 | %th= model_class.human_attribute_name(:created_at)
10 | %th=t '.actions', default: t("helpers.actions")
11 | %tbody
12 | - @users.each do |user|
13 | %tr
14 | %td= link_to user.id, user_path(user)
15 | %td= link_to user.email, user_path(user)
16 | %td= user.created_at
17 | %td
18 | = link_to 'Edit', edit_user_path(user), class: 'btn btn-mini'
19 | = link_to 'Destroy', user_path(user), method: :delete, data: {confirm: 'Are you sure?'}, class: 'btn btn-mini btn-danger'
20 |
21 | = link_to 'New', new_user_path, class: 'btn btn-primary'
22 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe User do
4 | describe '#email' do
5 | it { should validate_uniqueness_of(:email) }
6 | it { should validate_presence_of(:email) }
7 | end
8 |
9 | describe "#customer" do
10 | it "returns nil if customer_id is not set" do
11 | User.new.customer.should be_nil
12 | end
13 |
14 | it "returns BraintreeRails::Customer if customer_id presents" do
15 | customer = BraintreeRails::Customer.new
16 | BraintreeRails::Customer.should_receive(:new).with('foo').and_return(customer)
17 | User.new(customer_id: 'foo').customer.should == customer
18 | end
19 |
20 | it "should delete customer if customer_id presents" do
21 | BraintreeRails::Customer.should_receive(:delete).with('foo')
22 | User.create(email: 'braintree-rails@example.com', customer_id: 'foo').destroy
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # This file is copied to spec/ when you run 'rails generate rspec:install'
2 | ENV["RAILS_ENV"] ||= 'test'
3 | require File.expand_path("../../config/environment", __FILE__)
4 | require 'rspec/rails'
5 | require 'rspec/autorun'
6 |
7 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
8 |
9 | RSpec.configure do |config|
10 | config.use_transactional_fixtures = true
11 | config.infer_base_class_for_anonymous_controllers = false
12 | config.order = "random"
13 |
14 | config.before(:all, type: :model) do
15 | BraintreeRails::Configuration.merchant_id = nil
16 | BraintreeRails::Configuration.public_key = nil
17 | BraintreeRails::Configuration.private_key = nil
18 | BraintreeRails::Configuration.client_side_encryption_key = nil
19 | end
20 |
21 | config.include(CapybaraHelper)
22 | config.include(BraintreeIntegration, braintree_integration: true)
23 | end
24 |
--------------------------------------------------------------------------------
/app/assets/javascripts/addresses.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
4 | jQuery ->
5 | $('#address_region').parent().parent().hide()
6 | states = $('#address_region').html()
7 | $('#address_country_code_alpha2').change ->
8 | country = $('#address_country_code_alpha2 :selected').text()
9 | escaped_country = country.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
10 | options = $(states).filter("optgroup[label=#{escaped_country}]").html()
11 | if options
12 | $('#address_region').html(options)
13 | $('#address_region').parent().parent().show()
14 | else
15 | $('#address_region').empty()
16 | $('#address_region').parent().parent().hide()
17 | $('#address_country_code_alpha2').change()
18 |
--------------------------------------------------------------------------------
/spec/support/braintree_integration/configuration.rb:
--------------------------------------------------------------------------------
1 | module BraintreeIntegration
2 | module Configuration
3 | def load_configuration
4 | @configuration = (YAML.load_file(braintree_auth_file) rescue {}).tap do |config|
5 | BraintreeRails::Configuration.merchant_id = config['merchant_id']
6 | BraintreeRails::Configuration.public_key = config['public_key']
7 | BraintreeRails::Configuration.private_key = config['private_key']
8 | BraintreeRails::Configuration.client_side_encryption_key = config['client_side_encryption_key']
9 | end
10 | end
11 |
12 | def configuration
13 | @configuration ||= load_configuration
14 | end
15 |
16 | def braintree_auth_file
17 | Rails.root.join("spec/support/braintree_integration/braintree_auth.yml")
18 | end
19 |
20 | def braintree_auth_set?
21 | BraintreeRails::Configuration.merchant_id.present? rescue false
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/assets/javascripts/transactions.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
4 | jQuery ->
5 | $('#transaction_billing_region').parent().parent().hide()
6 | states = $('#transaction_billing_region').html()
7 | $('#transaction_billing_country_code_alpha2').change ->
8 | country = $('#transaction_billing_country_code_alpha2 :selected').text()
9 | escaped_country = country.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
10 | options = $(states).filter("optgroup[label=#{escaped_country}]").html()
11 | if options
12 | $('#transaction_billing_region').html(options)
13 | $('#transaction_billing_region').parent().parent().show()
14 | else
15 | $('#transaction_billing_region').empty()
16 | $('#transaction_billing_region').parent().parent().hide()
--------------------------------------------------------------------------------
/app/views/subscriptions/_form.html.haml:
--------------------------------------------------------------------------------
1 | = simple_form_for @subscription, url: url, html: { class: 'form-horizontal' } do |f|
2 | - if @plan
3 | .control-group.string.optional.subscription_plan
4 | %label.string.optional.control-label{for: 'subscription_plan'}= 'Plan'
5 | .controls
6 | %p#subscription_plan.string.optional= @plan.name
7 | - else
8 | = f.input :plan_id, options_for_plan_select
9 | - if @credit_card
10 | .control-group.string.optional.subscription_credit_card
11 | %label.string.optional.control-label{for: 'subscription_credit_card'}= 'CreditCard'
12 | .controls
13 | %p#subscription_credit_card.string.optional= @credit_card.masked_number
14 | - else
15 | = f.input :payment_method_token, options_for_credit_card_select(@customer || @subscription.credit_card.customer)
16 | = f.input :id
17 | = f.input :price
18 | .form-actions
19 | = f.button :submit, class: 'btn-primary'
20 | = link_to 'Cancel', subscriptions_path, class: 'btn'
21 |
--------------------------------------------------------------------------------
/app/views/subscriptions/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Subscription
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:id)
8 | %th= model_class.human_attribute_name(:price)
9 | %th= model_class.human_attribute_name(:status)
10 | %th= model_class.human_attribute_name(:balance)
11 | %th= 'Actions'
12 | %tbody
13 | - @subscriptions.each do |subscription|
14 | %tr
15 | %td= link_to subscription.id, subscription_path(subscription)
16 | %td= subscription.price
17 | %td= subscription.status
18 | %td= subscription.balance
19 | %td
20 | = link_to 'Edit', edit_subscription_path(subscription), class: 'btn btn-mini'
21 | = link_to 'Destroy', subscription_path(subscription), method: :delete, class: 'btn btn-mini btn-danger'
22 | = link_to 'Back', back_path, class: 'btn'
23 | = link_to 'New', new_subscription_path, class: 'btn btn-primary' if @customer.present?
24 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module BraintreeRailsExample
10 | class Application < Rails::Application
11 | # Settings in config/environments/* take precedence over those specified here.
12 | # Application configuration should go into files in config/initializers
13 | # -- all .rb files in that directory are automatically loaded.
14 |
15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
17 | # config.time_zone = 'Central Time (US & Canada)'
18 |
19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
21 | # config.i18n.default_locale = :de
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/assets/javascripts/credit_cards.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
4 | jQuery ->
5 | $('#credit_card_billing_address_region').parent().parent().hide()
6 | states = $('#credit_card_billing_address_region').html()
7 | $('#credit_card_billing_address_country_code_alpha2').change ->
8 | country = $('#credit_card_billing_address_country_code_alpha2 :selected').text()
9 | escaped_country = country.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1')
10 | options = $(states).filter("optgroup[label=#{escaped_country}]").html()
11 | if options
12 | $('#credit_card_billing_address_region').html(options)
13 | $('#credit_card_billing_address_region').parent().parent().show()
14 | else
15 | $('#credit_card_billing_address_region').empty()
16 | $('#credit_card_billing_address_region').parent().parent().hide()
17 | $('#credit_card_billing_address_country_code_alpha2').change()
18 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Lin Yang http://linyang.me
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/spec/support/braintree_integration/test_data.rb:
--------------------------------------------------------------------------------
1 | module BraintreeIntegration
2 | module TestData
3 | def customer_hash
4 | {
5 | first_name: "Brain",
6 | last_name: "Tree",
7 | email: 'braintree-rails@example.com'
8 | }
9 | end
10 |
11 | def address_hash
12 | {
13 | first_name: 'Brain',
14 | last_name: 'Tree',
15 | company: 'Braintree',
16 | street_address: "1134 Crane Avenue",
17 | extended_address: "Suite 200",
18 | locality: 'Menlo Park',
19 | region: 'California',
20 | postal_code: '94025',
21 | country_code_alpha2: 'US'
22 | }
23 | end
24 |
25 | def credit_card_hash
26 | {
27 | number: (Braintree::Test::CreditCardNumbers::All - Braintree::Test::CreditCardNumbers::AmExes).shuffle.first,
28 | cvv: ("000".."999").to_a.shuffle.first,
29 | cardholder_name: 'Brain Tree',
30 | expiration_month: ("01".."12").to_a.shuffle.first,
31 | expiration_year: ("2012".."2035").to_a.shuffle.first,
32 | billing_address: address_hash,
33 | }
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/app/views/addresses/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Address
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:full_name)
8 | %th= model_class.human_attribute_name(:company)
9 | %th= model_class.human_attribute_name(:street_address)
10 | %th= model_class.human_attribute_name(:extended_address)
11 | %th= 'Actions'
12 | %tbody
13 | - @addresses.each do |address|
14 | %tr
15 | %td= address.full_name
16 | %td= address.company
17 | %td= link_to address.street_address, user_customer_address_path(@user, address)
18 | %td= address.extended_address
19 | %td
20 | = link_to 'Edit', edit_user_customer_address_path(@user, address), class: 'btn btn-mini'
21 | = link_to 'Destroy', user_customer_address_path(@user, address), method: :delete, data: {confirm:'Are you sure?'}, class: 'btn btn-mini btn-danger'
22 |
23 | = link_to 'Back', user_customer_path(@user), class: 'btn'
24 | = link_to 'New', new_user_customer_address_path(@user), class: 'btn btn-primary'
25 |
--------------------------------------------------------------------------------
/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | class UsersController < ApplicationController
2 | before_filter :find_user, except: [:index, :new, :create]
3 |
4 | def index
5 | @users = User.all
6 | end
7 |
8 | def new
9 | @user = User.new
10 | end
11 |
12 | def edit; end
13 |
14 | def show; end
15 |
16 | def create
17 | @user = User.new(user_params)
18 | if @user.save
19 | redirect_to @user, notice: 'User was successfully created.'
20 | else
21 | flash.now[:alert] = @user.errors.full_messages.join(".\n")
22 | render action: "new"
23 | end
24 | end
25 |
26 | def update
27 | if @user.update_attributes(user_params)
28 | redirect_to @user, notice: 'User was successfully updated.'
29 | else
30 | flash.now[:alert] = @user.errors.full_messages.join(".\n")
31 | render action: "edit"
32 | end
33 | end
34 |
35 | def destroy
36 | @user.destroy
37 | redirect_to users_url, notice: 'User was successfully destroyed.'
38 | end
39 |
40 | private
41 | def find_user
42 | @user = User.find(params[:id])
43 | end
44 |
45 | def user_params
46 | params.require(:user).permit(:name, :email)
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | activemodel:
6 | attributes:
7 | braintree_rails/address:
8 | locality: "City"
9 | country_name: "Country"
10 | country_code_alpha2: "Country"
11 | country_code_alpha3: "Country"
12 | country_code_numeric: "Country"
13 | braintree_rails/credit_card:
14 | cvv: "CVV"
15 | errors:
16 | models:
17 | braintree_rails/credit_card:
18 | attributes:
19 | number:
20 | '81715': "is not luhn 10 valid."
21 | '81716': "should be between 12 to 19 digits."
22 | braintree_rails/address:
23 | attributes:
24 | country_name:
25 | inclusion: "%{value} is not supported yet."
26 | country_code_alpha2:
27 | inclusion: "%{value} is not supported yet."
28 | country_code_alpha3:
29 | inclusion: "%{value} is not supported yet."
30 | country_code_numeric:
31 | inclusion: "%{value} is not supported yet."
32 |
--------------------------------------------------------------------------------
/app/views/credit_cards/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::CreditCard
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:card_type)
8 | %th= model_class.human_attribute_name(:masked_number)
9 | %th= model_class.human_attribute_name(:cardholder_name)
10 | %th= model_class.human_attribute_name(:expiration_date)
11 | %th= 'Actions'
12 | %tbody
13 | - @credit_cards.each do |credit_card|
14 | %tr
15 | %td= credit_card.card_type
16 | %td= link_to credit_card.masked_number, user_customer_credit_card_path(@user, credit_card)
17 | %td= credit_card.cardholder_name
18 | %td= credit_card.expiration_date
19 | %td
20 | = link_to 'Edit', edit_user_customer_credit_card_path(@user, credit_card), class: 'btn btn-mini'
21 | = link_to 'Destroy', user_customer_credit_card_path(@user, credit_card), method: :delete, data: {confirm:'Are you sure?'}, class: 'btn btn-mini btn-danger'
22 |
23 | = link_to 'Back', user_customer_path(@user), class: 'btn'
24 | = link_to 'New', new_user_customer_credit_card_path(@user), class: 'btn btn-primary'
25 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | BraintreeRailsExample::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | # This option may cause significant delays in view rendering with a large
27 | # number of complex assets.
28 | config.assets.debug = true
29 | end
30 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended to check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(version: 20130322050449) do
15 |
16 | create_table "history_events", force: true do |t|
17 | t.string "model"
18 | t.text "data"
19 | t.datetime "created_at", null: false
20 | t.datetime "updated_at", null: false
21 | end
22 |
23 | create_table "users", force: true do |t|
24 | t.string "email"
25 | t.string "customer_id"
26 | t.datetime "created_at", null: false
27 | t.datetime "updated_at", null: false
28 | end
29 |
30 | end
31 |
--------------------------------------------------------------------------------
/app/views/credit_cards/show.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::CreditCard
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human
4 | - [:card_type, :masked_number, :cardholder_name, :expiration_date].each do |attribute|
5 | %p
6 | %strong= model_class.human_attribute_name(attribute) + ':'
7 | %br
8 | = @credit_card.send(attribute)
9 | - [:first_name, :last_name, :company, :street_address, :extended_address, :locality, :region, :postal_code, :country_name].each do |billing_address_attribute|
10 | %p
11 | %strong= BraintreeRails::Address.human_attribute_name(billing_address_attribute) + ':'
12 | %br
13 | = @credit_card.billing_address.send(billing_address_attribute) if @credit_card.billing_address
14 |
15 | .form-actions
16 | = link_to 'Back', user_customer_credit_cards_path(@user), class: 'btn'
17 | = link_to 'Edit', edit_user_customer_credit_card_path(@user, @credit_card), class: 'btn'
18 | = link_to 'Transactions', user_customer_credit_card_transactions_path(@user, @credit_card), class: 'btn'
19 | = link_to 'Subscriptions', user_customer_credit_card_subscriptions_path(@user, @credit_card), class: 'btn'
20 | = link_to 'Destroy', user_customer_credit_card_path(@user, @credit_card), method: "delete", data: {confirm: 'Are you sure?'}, class: 'btn btn-danger'
21 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/scaffolds.css.less:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #fff;
3 | color: #333;
4 | font-family: verdana, arial, helvetica, sans-serif;
5 | font-size: 13px;
6 | line-height: 18px;
7 | }
8 |
9 | p, ol, ul, td {
10 | font-family: verdana, arial, helvetica, sans-serif;
11 | font-size: 13px;
12 | line-height: 18px;
13 | }
14 |
15 | pre {
16 | background-color: #eee;
17 | padding: 10px;
18 | font-size: 11px;
19 | }
20 |
21 | a {
22 | color: #000;
23 | &:visited {
24 | color: #666;
25 | }
26 | &:hover {
27 | color: #fff;
28 | background-color: #000;
29 | }
30 | }
31 |
32 | div {
33 | &.field, &.actions {
34 | margin-bottom: 10px;
35 | }
36 | }
37 |
38 | #notice {
39 | color: green;
40 | }
41 |
42 | .field_with_errors {
43 | padding: 2px;
44 | background-color: red;
45 | display: table;
46 | }
47 |
48 | #error_explanation {
49 | width: 450px;
50 | border: 2px solid red;
51 | padding: 7px;
52 | padding-bottom: 0;
53 | margin-bottom: 20px;
54 | background-color: #f0f0f0;
55 | h2 {
56 | text-align: left;
57 | font-weight: bold;
58 | padding: 5px 5px 5px 15px;
59 | font-size: 12px;
60 | margin: -7px;
61 | margin-bottom: 0px;
62 | background-color: #c00;
63 | color: #fff;
64 | }
65 | ul li {
66 | font-size: 12px;
67 | list-style: square;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/views/transactions/index.html.haml:
--------------------------------------------------------------------------------
1 | - model_class = BraintreeRails::Transaction
2 | .page-header
3 | %h1=t '.title', default: model_class.model_name.human.pluralize
4 | %table.table.table-striped
5 | %thead
6 | %tr
7 | %th= model_class.human_attribute_name(:id)
8 | %th= model_class.human_attribute_name(:amount)
9 | %th= model_class.human_attribute_name(:status)
10 | %th= model_class.human_attribute_name(:customer)
11 | %th= model_class.human_attribute_name(:credit_card)
12 | %th= 'Actions'
13 | %tbody
14 | - @transactions.each do |transaction|
15 | %tr
16 | %td= link_to transaction.id, transaction_path(transaction)
17 | %td= transaction.amount
18 | %td= transaction.status
19 | %td= transaction.customer.full_name
20 | %td= transaction.credit_card.masked_number
21 | %td
22 | = link_to 'Submit', transaction_path(transaction, operation: 'submit_for_settlement'), method: :put, class: 'btn btn-mini btn-primary'
23 | = link_to 'Void', transaction_path(transaction, operation: 'void'), method: :put, class: 'btn btn-mini btn-warning'
24 | = link_to 'Refund', transaction_path(transaction, operation: 'refund'), method: :put, class: 'btn btn-mini btn-danger'
25 |
26 | = link_to 'Back', back_path, class: 'btn'
27 | = link_to 'New', new_transaction_path, class: 'btn btn-primary'
28 |
--------------------------------------------------------------------------------
/spec/support/braintree_integration/setup.rb:
--------------------------------------------------------------------------------
1 | module BraintreeIntegration
2 | module Setup
3 | def braintree_integration_load_configuration
4 | load_configuration
5 | return if braintree_auth_set?
6 | pending("You need to provide real credentials in #{braintree_auth_file} to run #{self.class.description}")
7 | end
8 |
9 | def braintree_integration_setup_test_data
10 | @user = User.create(email: 'braintree-rails@example.com')
11 | create_customer_for(@user)
12 | end
13 |
14 | def braintree_integration_teardown
15 | Capybara.using_driver(:selenium) do
16 | login_into_control_panel
17 | purge_sandbox_data
18 | end
19 | end
20 |
21 | def purge_sandbox_data
22 | page.execute_script("$('#purge_sandbox_data_form').submit()")
23 | find('.ui-dialog-buttonset').find('span', text: 'Yes').click
24 | end
25 |
26 | def login_into_control_panel
27 | visit 'https://sandbox.braintreegateway.com/'
28 | fill_in_all(nil, configuration.slice('login', 'password'))
29 | click_button 'Sign In'
30 | end
31 |
32 | def self.included(receiver)
33 | receiver.before(:all) { braintree_integration_load_configuration }
34 | receiver.before(:each) { braintree_integration_setup_test_data }
35 | receiver.after(:all) { braintree_integration_teardown }
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/bootstrap_and_overrides.css.less:
--------------------------------------------------------------------------------
1 | @import "twitter/bootstrap/bootstrap";
2 | @import "twitter/bootstrap/responsive";
3 |
4 | // Set the correct sprite paths
5 | @iconSpritePath: image-url("twitter/bootstrap/glyphicons-halflings.png");
6 | @iconWhiteSpritePath: image-url("twitter/bootstrap/glyphicons-halflings-white.png");
7 |
8 | // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
9 | @fontAwesomeEotPath: asset-url("fontawesome-webfont.eot");
10 | @fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot?#iefix");
11 | @fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff");
12 | @fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf");
13 | @fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular");
14 |
15 | // Font Awesome
16 | @import "fontawesome/font-awesome";
17 |
18 | // Glyphicons
19 | //@import "twitter/bootstrap/sprites.less";
20 |
21 | // Your custom LESS stylesheets goes here
22 | //
23 | // Since bootstrap was imported above you have access to its mixins which
24 | // you may use and inherit here
25 | //
26 | // If you'd like to override bootstrap's own variables, you can do so here as well
27 | // See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation
28 | //
29 | // Example:
30 | // @linkColor: #ff0000;
31 |
32 | body {
33 | padding-top: 60px;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/app/views/transactions/_form.html.haml:
--------------------------------------------------------------------------------
1 | = simple_form_for @transaction, url: transactions_path, html: { id: 'braintree-transaction-form', class: 'form-horizontal' } do |f|
2 | - if @credit_card
3 | = f.input :amount, hint: @credit_card.masked_number
4 | - elsif @customer
5 | = f.input :amount
6 | = f.input :credit_card, options_for_credit_card_select(@customer)
7 | - else
8 | = f.input :amount
9 | = f.simple_fields_for :credit_card, @transaction.credit_card do |cc|
10 | = cc.input :number, input_html: { 'data-encrypted-name' => true }
11 | = cc.input :cardholder_name
12 | = cc.input :cvv, maxlength: 4, label: 'CVV', input_html: { 'data-encrypted-name' => true }
13 | = cc.input :expiration_month, options_for_month_select.merge(input_html: { 'data-encrypted-name' => true })
14 | = cc.input :expiration_year, options_for_year_select.merge(input_html: { 'data-encrypted-name' => true })
15 | = f.simple_fields_for :billing, @transaction.billing do |ba|
16 | = ba.input :first_name
17 | = ba.input :last_name
18 | = ba.input :company
19 | = ba.input :street_address
20 | = ba.input :extended_address
21 | = ba.input :locality, label: 'City'
22 | = ba.input :country_code_alpha2, options_for_country_select
23 | = ba.input :region, options_for_region_select
24 | = ba.input :postal_code
25 |
26 | .form-actions
27 | = f.button :submit, class: 'btn-primary'
28 | = link_to 'Cancel', transactions_path, class: 'btn'
29 |
--------------------------------------------------------------------------------
/app/views/credit_cards/_form.html.haml:
--------------------------------------------------------------------------------
1 | = simple_form_for @credit_card, url: url, html: { class: 'form-horizontal', id: 'braintree-credit-card-form' } do |f|
2 | = f.input(:number, input_html: { 'data-encrypted-name' => true }) if @credit_card.new_record?
3 | = f.input :cardholder_name
4 | = f.input(:cvv, maxlength: 4, input_html: {'data-encrypted-name' => true }) if @credit_card.new_record?
5 | = f.input :expiration_month, options_for_month_select
6 | = f.input :expiration_year, options_for_year_select
7 | = f.simple_fields_for :billing_address, @credit_card.billing_address do |ba|
8 | = ba.input :first_name
9 | = ba.input :last_name
10 | = ba.input :company
11 | = ba.input :street_address
12 | = ba.input :extended_address
13 | = ba.input :locality
14 | = ba.input :country_code_alpha2, options_for_country_select
15 | = ba.input :region, options_for_region_select
16 | = ba.input :postal_code
17 | .form-actions
18 | = f.button :submit, class: 'btn-primary'
19 | = link_to 'Cancel', user_customer_credit_cards_path(@user), class: 'btn'
20 |
21 | - content_for :javascript do
22 | :javascript
23 | var braintree = Braintree.create("#{BraintreeRails::Configuration.client_side_encryption_key}")
24 | braintree.onSubmitEncryptForm('braintree-credit-card-form');
25 | BraintreeData.setup("#{BraintreeRails::Configuration.merchant_id}", "braintree-credit-card-form", BraintreeData.environments.sandbox)
26 | BraintreeData.setup("#{BraintreeRails::Configuration.merchant_id}", "braintree-transaction-form", BraintreeData.environments.sandbox)
27 |
--------------------------------------------------------------------------------
/app/controllers/customers_controller.rb:
--------------------------------------------------------------------------------
1 | class CustomersController < ApplicationController
2 | before_filter :find_user
3 | before_filter :find_customer, except: [:new, :create]
4 |
5 | def new
6 | @customer = BraintreeRails::Customer.new(email: @user.email)
7 | end
8 |
9 | def create
10 | @customer = BraintreeRails::Customer.new(params[:customer].merge(params.slice(:device_data)))
11 | if @customer.save
12 | @user.update_attribute(:customer_id, @customer.id)
13 | flash[:notice] = "Customer has been successfully created."
14 | redirect_to user_customer_path(@user) and return
15 | else
16 | flash[:alert] = @customer.errors.full_messages.join(".\n")
17 | render :new
18 | end
19 | end
20 |
21 | def show
22 | if @customer.nil?
23 | redirect_to new_user_customer_path(@user) and return
24 | end
25 | end
26 |
27 | def edit; end
28 |
29 | def update
30 | if @customer.update_attributes(params[:customer].merge(params.slice(:device_data)))
31 | flash[:notice] = "Customer has been successfully updated."
32 | redirect_to user_customer_path(@user) and return
33 | else
34 | flash[:alert] = @customer.errors.full_messages.join(".\n")
35 | render :edit
36 | end
37 | end
38 |
39 | def destroy
40 | @customer.destroy
41 | @user.update_attribute(:customer_id, nil)
42 | flash[:notice] = "Customer has been successfully deleted."
43 | redirect_to user_path(@user)
44 | end
45 |
46 | protected
47 | def find_user
48 | @user = User.find(params[:user_id])
49 | end
50 |
51 | def find_customer
52 | @customer = @user.customer
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/spec/features/user_management_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "User Management" do
4 | before(:each) do
5 | @user = User.create!(email: 'braintree-rails@example.com')
6 | end
7 |
8 | it "creates user by given email" do
9 | visit users_path
10 | click_link 'New'
11 | fill_in 'Email', with: 'braintree-rails@example.org'
12 | click_button 'Create User'
13 | current_path.should == user_path(User.last)
14 | page.should have_content('User was successfully created.')
15 | page.should have_content('braintree-rails@example.org')
16 | end
17 |
18 | it "shows given user" do
19 | visit user_path(@user)
20 | page.should have_content('braintree-rails@example.com')
21 | page.should have_link('Back')
22 | page.should have_link('Edit', href: edit_user_path(@user))
23 | page.should have_link('Customer', href: user_customer_path(@user))
24 | page.should have_link('Destroy')
25 | end
26 |
27 | it "shows list of users" do
28 | visit users_path
29 | page.should have_link(@user.email, href: user_path(@user))
30 | end
31 |
32 | it "update given user" do
33 | visit edit_user_path(@user)
34 | fill_in 'Email', with: 'braintree-rails@example.org'
35 | click_button 'Update User'
36 | current_path.should == user_path(@user)
37 | page.should have_content('User was successfully updated.')
38 | page.should have_content('braintree-rails@example.org')
39 | end
40 |
41 | it "destroys given user" do
42 | visit user_path(@user)
43 | click_link 'Destroy'
44 | page.should have_content('User was successfully destroyed.')
45 | User.exists?(@user.id).should be_false
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/app/controllers/addresses_controller.rb:
--------------------------------------------------------------------------------
1 | class AddressesController < ApplicationController
2 | before_filter :find_user, :find_customer
3 | before_filter :find_address, except: [:new, :create]
4 |
5 | def index
6 | @addresses = @customer.addresses
7 | end
8 |
9 | def new
10 | @address = @customer.addresses.build(country_code_alpha2: 'US', region: 'California')
11 | end
12 |
13 | def edit; end
14 |
15 | def show; end
16 |
17 | def create
18 | @address = @customer.addresses.build(params[:address])
19 | if @address.save
20 | flash[:notice] = "Address has been successfully created."
21 | redirect_to user_customer_address_path(@user, @address)
22 | else
23 | flash[:alert] = @address.errors.full_messages.join(".\n")
24 | render :new
25 | end
26 | end
27 |
28 | def update
29 | if @address.update_attributes(params[:address])
30 | flash[:notice] = "Address has been successfully updated."
31 | redirect_to user_customer_address_path(@user, @address) and return
32 | else
33 | flash[:alert] = @address.errors.full_messages.join(".\n")
34 | render :edit
35 | end
36 | end
37 |
38 | def destroy
39 | @address.destroy
40 | flash[:notice] = "Address has been successfully deleted."
41 | redirect_to user_customer_addresses_path(@user)
42 | end
43 |
44 | protected
45 | def find_user
46 | @user = User.find(params[:user_id])
47 | end
48 |
49 | def find_customer
50 | @customer = @user.customer
51 | redirect_to user_path(@user) and return if @customer.nil?
52 | end
53 |
54 | def find_address
55 | @address = @customer.addresses.find(params[:id])
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | BraintreeRailsExample::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static asset server for tests with Cache-Control for performance.
16 | config.serve_static_assets = true
17 | config.static_cache_control = "public, max-age=3600"
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Print deprecation notices to the stderr.
35 | config.active_support.deprecation = :stderr
36 | end
37 |
--------------------------------------------------------------------------------
/app/controllers/credit_cards_controller.rb:
--------------------------------------------------------------------------------
1 | class CreditCardsController < ApplicationController
2 | before_filter :find_user, :find_customer
3 | before_filter :find_credit_card, except: [:new, :create]
4 |
5 | def index
6 | @credit_cards = @customer.credit_cards
7 | end
8 |
9 | def new
10 | billing_address = @customer.addresses.first || {}
11 | @credit_card = @customer.credit_cards.build({cardholder_name: @customer.full_name, billing_address: billing_address})
12 | end
13 |
14 | def edit; end
15 |
16 | def show; end
17 |
18 | def create
19 | @credit_card = @customer.credit_cards.build(params[:credit_card].merge(params.slice(:device_data)))
20 | if @credit_card.save
21 | flash[:notice] = "Credit card has been successfully updated."
22 | redirect_to user_customer_credit_card_path(@user, @credit_card)
23 | else
24 | flash[:alert] = @credit_card.errors.full_messages.join(".\n")
25 | render :new
26 | end
27 | end
28 |
29 | def update
30 | if @credit_card.update_attributes(params[:credit_card].merge(params.slice(:device_data)))
31 | flash[:notice] = "Credit card has been successfully updated."
32 | redirect_to user_customer_credit_card_path(@user, @credit_card) and return
33 | else
34 | flash[:alert] = @credit_card.errors.full_messages.join(".\n")
35 | render :edit
36 | end
37 | end
38 |
39 | def destroy
40 | @credit_card.destroy
41 | flash[:notice] = "Credit card has been successfully deleted."
42 | redirect_to user_customer_credit_cards_path(@user)
43 | end
44 |
45 | protected
46 | def find_user
47 | @user = User.find(params[:user_id])
48 | end
49 |
50 | def find_customer
51 | @customer = @user.customer
52 | redirect_to user_path(@user) and return if @customer.nil?
53 | end
54 |
55 | def find_credit_card
56 | @credit_card = @customer.credit_cards.find(params[:id])
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/spec/features/customer_management_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Customer Management", braintree_integration: true do
4 |
5 | it "redirects to new customer path if not present" do
6 | user = User.create(email: 'braintree-rails@example.org')
7 | visit user_customer_path(user)
8 | current_path.should == new_user_customer_path(user)
9 | find_field('Email').value.should == user.email
10 | end
11 |
12 | it "creates customer by given attributes" do
13 | user = User.create(email: 'braintree-rails@example.org')
14 | visit new_user_customer_path(user)
15 | fill_in_all(:customer, customer_hash)
16 | click_button('Create Customer')
17 | current_path.should == user_customer_path(user)
18 | page.should have_content('Customer has been successfully created.')
19 |
20 | user.reload.customer.should_not be_nil
21 | customer_hash.each do |attribute, value|
22 | user.customer.attributes[attribute].should == value
23 | end
24 | end
25 |
26 | it "shows customer attributes" do
27 | visit user_customer_path(@user)
28 | customer_hash.values.each do |value|
29 | page.should have_content(value)
30 | end
31 | end
32 |
33 | it "updates customer by given attributes" do
34 | visit edit_user_customer_path(@user)
35 | fill_in_all(:customer, email: 'braintree-rails@example.org')
36 | click_button('Update Customer')
37 | page.should have_content('Customer has been successfully updated.')
38 | page.should have_content('braintree-rails@example.org')
39 | end
40 |
41 | it "destroys given customer" do
42 | customer = @user.customer
43 | visit user_customer_path(@user)
44 | click_link("Destroy")
45 | current_path.should == user_path(@user)
46 | page.should have_content('Customer has been successfully deleted.')
47 | expect { BraintreeRails::Customer.find customer.id }.to raise_error(Braintree::NotFoundError)
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/spec/features/address_management_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Address Management", braintree_integration: true do
4 |
5 | it "shows a list of addresses of given customer" do
6 | address = @user.customer.addresses.create!(address_hash)
7 | visit user_customer_addresses_path(@user)
8 | page.should have_link(address.street_address, href: user_customer_address_path(@user, address))
9 | end
10 |
11 | it "creates address by given attributes" do
12 | visit new_user_customer_address_path(@user)
13 | fill_in_all(:address, address_hash)
14 | click_button('Create Address')
15 | page.should have_content('Address has been successfully created.')
16 |
17 | address = @user.customer.addresses.find(current_path.split('/').last)
18 |
19 | address_hash.each do |attribute, value|
20 | address.attributes[attribute].should == value
21 | end
22 | end
23 |
24 | it "shows address attributes" do
25 | address = @user.customer.addresses.create!(address_hash)
26 | visit user_customer_address_path(@user, address)
27 | address_hash.values.each do |value|
28 | page.should have_content(value)
29 | end
30 | end
31 |
32 | it "updates address by given attributes" do
33 | address = @user.customer.addresses.create!(address_hash)
34 | visit edit_user_customer_address_path(@user, address)
35 | fill_in_all(:address, postal_code: '12345')
36 | click_button('Update Address')
37 | page.should have_content('Address has been successfully updated.')
38 | page.should have_content('12345')
39 | end
40 |
41 | it "destroys given address" do
42 | address = @user.customer.addresses.create!(address_hash)
43 | visit user_customer_address_path(@user, address)
44 | click_link("Destroy")
45 | current_path.should == user_customer_addresses_path(@user)
46 | page.should have_content('Address has been successfully deleted.')
47 | expect { BraintreeRails::Address.find @user.customer.id, address.id }.to raise_error(Braintree::NotFoundError)
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to braintree-rails-example
2 | [braintree-rails-example](https://github.com/lyang/braintree-rails-example) is a demo rails app that uses [braintree-rails](https://github.com/lyang/braintree-rails) gem to intergrate with Braintree payment api.
3 |
4 | This demo app shows how [braintree-rails](https://github.com/lyang/braintree-rails-example) simplified the code you needed in model/view/controller.
5 |
6 | With the "railsy" models [braintree-rails](https://github.com/lyang/braintree-rails) provides, you can use them exactly "the rails way" as you would with any other ActiveRecord object.
7 |
8 | In addition, the app uses Braintree.js for credit card data encryption. It helps you greatly reduce the PCI scope while keeps the flexibility.
9 |
10 | ## Instructions
11 | Clone the repo
12 |
13 | git clone git://github.com/lyang/braintree-rails-example.git
14 | bundle install
15 |
16 | Setup the database
17 |
18 | rake db:migrate
19 |
20 | Then you need to edit `config/braintree.rb` to put in your Braintree sandbox credentials
21 | ```ruby
22 | # Those are just delegation to Braintree::Configuration
23 | BraintreeRails::Configuration.environment = :sandbox
24 | BraintreeRails::Configuration.logger = Logger.new('log/braintree.log')
25 | BraintreeRails::Configuration.merchant_id = ENV['MERCHANT_ID']
26 | BraintreeRails::Configuration.public_key = ENV['PUBLIC_KEY']
27 | BraintreeRails::Configuration.private_key = ENV['PRIVATE_KEY']
28 |
29 | # This is just a convenient place you can put your CSE key
30 | BraintreeRails::Configuration.client_side_encryption_key = ENV['CLIENT_SIDE_ENCRYPTION_KEY']
31 | ```
32 |
33 | Then you can start the demo with
34 |
35 | rails s
36 |
37 | ## Live demo
38 | You can play with the live demo at [here](http://braintree-rails-example.herokuapp.com/).
39 |
40 | **Caultion**
41 | The live demo is for public *demo purpose only*.
42 |
43 | **DO NOT** put any important or sensitive info in this demo.
44 |
45 | It will be purged periodically.
46 |
47 | ## Sandbox environment for testing
48 | You can get your sandbox env at https://www.braintreepayments.com/get-started
49 |
--------------------------------------------------------------------------------
/app/controllers/transactions_controller.rb:
--------------------------------------------------------------------------------
1 | class TransactionsController < ApplicationController
2 | before_filter :find_transactions
3 | before_filter :find_transaction, except: [:new, :create]
4 | before_filter :restricted_update, only: :update
5 | helper_method :transaction_path, :transactions_path
6 |
7 | def index; end
8 |
9 | def show; end
10 |
11 | def new
12 | @transaction = @transactions.build(amount: rand(1..25))
13 | end
14 |
15 | def create
16 | @transaction = @transactions.build(params[:transaction].merge(params.slice(:device_data)))
17 | if @transaction.save
18 | flash[:notice] = "Transaction has been successfully created."
19 | redirect_to transaction_path(@transaction)
20 | else
21 | flash[:alert] = @transaction.errors.full_messages.join(".\n")
22 | render :new
23 | end
24 | end
25 |
26 | def update
27 | if @transaction.send(params[:operation])
28 | flash[:notice] = "Transaction has been #{params[:operation]}."
29 | else
30 | flash[:alert] = @transaction.errors.full_messages.join(".\n")
31 | end
32 | redirect_to transactions_path
33 | end
34 |
35 | protected
36 |
37 | def find_transactions
38 | if params[:user_id].present?
39 | @user = User.find(params[:user_id])
40 | @customer = @user.customer if @user
41 | @credit_card = @customer.credit_cards.find(params[:credit_card_id]) if params[:credit_card_id].present?
42 | @transactions = @credit_card.present? ? @credit_card.transactions : @customer.transactions
43 | else
44 | @transactions = BraintreeRails::Transactions.new(nil)
45 | end
46 | end
47 |
48 | def find_transaction
49 | @transaction = @transactions.find(params[:id])
50 | end
51 |
52 | def restricted_update
53 | return true if ['submit_for_settlement', 'void', 'refund'].include?(params[:operation])
54 | flash[:alert] = "Unknow operation: #{params[:operation]}!"
55 | redirect_to transactions_path and return
56 | end
57 |
58 | def transactions_path
59 | path ||= user_customer_credit_card_transactions_path(@user, @credit_card) if @credit_card
60 | path ||= user_customer_transactions_path(@user) if @user
61 | path ||= super
62 | end
63 |
64 |
65 | def transaction_path(transaction, options={})
66 | path ||= user_customer_credit_card_transaction_path(@user, @credit_card, transaction, options) if @credit_card
67 | path ||= user_customer_transaction_path(@user, transaction, options) if @user
68 | path ||= super(transaction, options)
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/public/assets/manifest-500885f534fb666445deb60092afb357.json:
--------------------------------------------------------------------------------
1 | {"files":{"rails-2da85d4b70342e6b8b7ec929d911af9f.png":{"logical_path":"rails.png","mtime":"2012-12-17T08:55:51-08:00","size":6646,"digest":"2da85d4b70342e6b8b7ec929d911af9f"},"application-6e3658cb51743044eed0ffab7c61d512.js":{"logical_path":"application.js","mtime":"2014-04-04T14:14:06-07:00","size":426413,"digest":"6e3658cb51743044eed0ffab7c61d512"},"application-d8aebfd478eee5c4f8dbe8411bcde500.css":{"logical_path":"application.css","mtime":"2014-04-04T14:47:46-07:00","size":165637,"digest":"d8aebfd478eee5c4f8dbe8411bcde500"},"fontawesome-webfont-658b64bfa55eff9dcd175bfd3ac3238a.eot":{"logical_path":"fontawesome-webfont.eot","mtime":"2014-04-04T11:39:40-07:00","size":37405,"digest":"658b64bfa55eff9dcd175bfd3ac3238a"},"fontawesome-webfont-8b61acf61f1d94a9a5dee063074e6b7a.svg":{"logical_path":"fontawesome-webfont.svg","mtime":"2014-04-04T11:39:40-07:00","size":197829,"digest":"8b61acf61f1d94a9a5dee063074e6b7a"},"fontawesome-webfont-efc9e487a6c573ca65c0d1a10c09f63d.ttf":{"logical_path":"fontawesome-webfont.ttf","mtime":"2014-04-04T11:39:40-07:00","size":79076,"digest":"efc9e487a6c573ca65c0d1a10c09f63d"},"fontawesome-webfont-98d61732d4d0af2312cfc7c1d005594b.woff":{"logical_path":"fontawesome-webfont.woff","mtime":"2014-04-04T11:39:40-07:00","size":43572,"digest":"98d61732d4d0af2312cfc7c1d005594b"},"twitter/bootstrap/glyphicons-halflings-white-e06c893995f68ff48aa1b2e591f27889.png":{"logical_path":"twitter/bootstrap/glyphicons-halflings-white.png","mtime":"2014-04-04T11:39:40-07:00","size":8777,"digest":"e06c893995f68ff48aa1b2e591f27889"},"twitter/bootstrap/glyphicons-halflings-d83dfe9df6cd3f50b5ae69abe919e065.png":{"logical_path":"twitter/bootstrap/glyphicons-halflings.png","mtime":"2014-04-04T11:39:40-07:00","size":12799,"digest":"d83dfe9df6cd3f50b5ae69abe919e065"}},"assets":{"rails.png":"rails-2da85d4b70342e6b8b7ec929d911af9f.png","application.js":"application-6e3658cb51743044eed0ffab7c61d512.js","application.css":"application-d8aebfd478eee5c4f8dbe8411bcde500.css","fontawesome-webfont.eot":"fontawesome-webfont-658b64bfa55eff9dcd175bfd3ac3238a.eot","fontawesome-webfont.svg":"fontawesome-webfont-8b61acf61f1d94a9a5dee063074e6b7a.svg","fontawesome-webfont.ttf":"fontawesome-webfont-efc9e487a6c573ca65c0d1a10c09f63d.ttf","fontawesome-webfont.woff":"fontawesome-webfont-98d61732d4d0af2312cfc7c1d005594b.woff","twitter/bootstrap/glyphicons-halflings-white.png":"twitter/bootstrap/glyphicons-halflings-white-e06c893995f68ff48aa1b2e591f27889.png","twitter/bootstrap/glyphicons-halflings.png":"twitter/bootstrap/glyphicons-halflings-d83dfe9df6cd3f50b5ae69abe919e065.png"}}
--------------------------------------------------------------------------------
/app/controllers/subscriptions_controller.rb:
--------------------------------------------------------------------------------
1 | class SubscriptionsController < ApplicationController
2 | before_filter :find_subscriptions
3 | before_filter :find_subscription, except: [:index, :create]
4 | helper_method :subscription_path, :subscriptions_path
5 |
6 | def index; end
7 |
8 | def show; end
9 |
10 | def edit; end
11 |
12 | def new
13 | @subscription = @subscriptions.build
14 | end
15 |
16 | def create
17 | @subscription = @subscriptions.build(params[:subscription])
18 | if @subscription.save
19 | flash[:notice] = "Subscription has been successfully created."
20 | redirect_to subscription_path(@subscription)
21 | else
22 | flash[:alert] = @subscription.errors.full_messages.join(".\n")
23 | render :new
24 | end
25 | end
26 |
27 | def update
28 | if @subscription.update_attributes(params[:subscription])
29 | flash[:notice] = "Customer has been successfully updated."
30 | redirect_to subscription_path(@subscription) and return
31 | else
32 | flash[:alert] = @subscription.errors.full_messages.join(".\n")
33 | render :edit
34 | end
35 | end
36 |
37 | def destroy
38 | @subscription.destroy
39 | flash[:notice] = "Subscription has been successfully canceled."
40 | redirect_to subscriptions_path
41 | end
42 |
43 | protected
44 |
45 | def find_subscriptions
46 | if find_credit_card
47 | @subscriptions = @credit_card.subscriptions
48 | elsif find_plan
49 | @subscriptions = @plan.subscriptions
50 | else
51 | @subscriptions = BraintreeRails::Subscriptions.new(nil)
52 | end
53 | end
54 |
55 | def find_subscription
56 | @subscription = @subscriptions.find(params[:id])
57 | end
58 |
59 | def find_credit_card
60 | @user = User.find(params[:user_id]) if params[:user_id].present?
61 | @customer = @user.customer if @user
62 | @credit_card = @customer.credit_cards.find(params[:credit_card_id]) if @customer && params[:credit_card_id].present?
63 | end
64 |
65 | def find_plan
66 | @plan = BraintreeRails::Plan.find(params[:plan_id]) if params[:plan_id].present?
67 | end
68 |
69 | def subscriptions_path
70 | path ||= user_customer_credit_card_subscriptions_path(@user, @credit_card) if @credit_card
71 | path ||= plan_subscriptions_path(@plan) if @plan
72 | path ||= super
73 | end
74 |
75 |
76 | def subscription_path(subscription, options={})
77 | path ||= user_customer_credit_card_subscription_path(@user, @credit_card, subscription, options) if @credit_card
78 | path ||= plan_subscription_path(@plan, subscription, options) if @plan
79 | path ||= super(subscription, options)
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.haml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | %html(lang="en")
3 | %head
4 | %meta(charset="utf-8")
5 | %meta(http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1")
6 | %meta(name="viewport" content="width=device-width, initial-scale=1.0")
7 | %title= content_for?(:title) ? yield(:title) : "BraintreeRailsExample"
8 | = csrf_meta_tags
9 | / Le HTML5 shim, for IE6-8 support of HTML elements
10 | /[if lt IE 9]
11 | = javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.6.1/html5shiv.js"
12 | = stylesheet_link_tag "application", :media => "all"
13 | = favicon_link_tag 'apple-touch-icon-144x144-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '144x144'
14 | = favicon_link_tag 'apple-touch-icon-114x114-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '114x114'
15 | = favicon_link_tag 'apple-touch-icon-72x72-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png', :sizes => '72x72'
16 | = favicon_link_tag 'apple-touch-icon-precomposed.png', :rel => 'apple-touch-icon-precomposed', :type => 'image/png'
17 | = favicon_link_tag 'favicon.ico', :rel => 'shortcut icon'
18 | = javascript_include_tag "application"
19 |
20 | %body
21 | .navbar.navbar-fixed-top
22 | .navbar-inner
23 | .container
24 | %a.btn.btn-navbar(data-target=".nav-collapse" data-toggle="collapse")
25 | %span.icon-bar
26 | %span.icon-bar
27 | %span.icon-bar
28 | %a.brand(href="/") Braintree Rails Example
29 | .container.nav-collapse
30 | %ul.nav
31 | %li= link_to("Users", users_path)
32 | %li= link_to("Transactions", transactions_path)
33 | %li= link_to("Plans", plans_path)
34 | %li= link_to("AddOns", add_ons_path)
35 | %li= link_to("Discounts", discounts_path)
36 | %li= link_to("Subscriptions", subscriptions_path)
37 | %li= link_to("History Events", history_events_path)
38 | %li= link_to("Source Code", "https://github.com/lyang/braintree-rails-example")
39 |
40 | .container
41 | = bootstrap_flash
42 | .content
43 | .row
44 | .span9
45 | = yield
46 | .span3
47 | .well.sidebar-nav
48 | %a.github{href: "https://github.com/lyang/braintree-rails"}
49 | %img{alt: "Fork me on GitHub", src: "https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"}
50 | %h3 Sidebar
51 | %ul.nav.nav-list
52 | %li.nav-header Sidebar
53 | %li= link_to("Users", users_path)
54 | %li= link_to("Transactions", transactions_path)
55 | %li= link_to("Plans", plans_path)
56 | %li= link_to("AddOns", add_ons_path)
57 | %li= link_to("Discounts", discounts_path)
58 | %li= link_to("Subscriptions", subscriptions_path)
59 | %li= link_to("History Events", history_events_path)
60 | %li= link_to("Source Code", "https://github.com/lyang/braintree-rails-example")
61 |
62 | %footer
63 | %p © Company 2014
64 | = yield :javascript
65 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | BraintreeRailsExample::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both thread web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application
18 | # Add `rack-cache` to your Gemfile before enabling this.
19 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
20 | # config.action_dispatch.rack_cache = true
21 |
22 | # Disable Rails's static asset server (Apache or nginx will already do this).
23 | config.serve_static_assets = false
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # Generate digests for assets URLs.
33 | config.assets.digest = true
34 |
35 | # Version of your assets, change this if you want to expire all your assets.
36 | config.assets.version = '1.0'
37 |
38 | # Specifies the header that your server uses for sending files.
39 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
41 |
42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
43 | # config.force_ssl = true
44 |
45 | # Set to :debug to see everything in the log.
46 | config.log_level = :info
47 |
48 | # Prepend all log lines with the following tags.
49 | # config.log_tags = [ :subdomain, :uuid ]
50 |
51 | # Use a different logger for distributed setups.
52 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
53 |
54 | # Use a different cache store in production.
55 | # config.cache_store = :mem_cache_store
56 |
57 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
58 | # config.action_controller.asset_host = "http://assets.example.com"
59 |
60 | # Precompile additional assets.
61 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
62 | # config.assets.precompile += %w( search.js )
63 |
64 | # Ignore bad email addresses and do not raise email delivery errors.
65 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
66 | # config.action_mailer.raise_delivery_errors = false
67 |
68 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
69 | # the I18n.default_locale when a translation can not be found).
70 | config.i18n.fallbacks = true
71 |
72 | # Send deprecation notices to registered listeners.
73 | config.active_support.deprecation = :notify
74 |
75 | # Disable automatic flushing of the log to improve performance.
76 | # config.autoflush_log = false
77 |
78 | # Use default logging formatter so that PID and timestamp are not suppressed.
79 | config.log_formatter = ::Logger::Formatter.new
80 | end
81 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/lyang/braintree-rails.git
3 | revision: c01fc805ab4f1f8d115377b9d0026d914b4c7963
4 | branch: master
5 | specs:
6 | braintree-rails (1.3.0)
7 | activemodel (>= 3.0)
8 | activesupport (>= 3.0)
9 | braintree (>= 2.28.0)
10 |
11 | GEM
12 | remote: https://rubygems.org/
13 | specs:
14 | actionmailer (4.0.4)
15 | actionpack (= 4.0.4)
16 | mail (~> 2.5.4)
17 | actionpack (4.0.4)
18 | activesupport (= 4.0.4)
19 | builder (~> 3.1.0)
20 | erubis (~> 2.7.0)
21 | rack (~> 1.5.2)
22 | rack-test (~> 0.6.2)
23 | activemodel (4.0.4)
24 | activesupport (= 4.0.4)
25 | builder (~> 3.1.0)
26 | activerecord (4.0.4)
27 | activemodel (= 4.0.4)
28 | activerecord-deprecated_finders (~> 1.0.2)
29 | activesupport (= 4.0.4)
30 | arel (~> 4.0.0)
31 | activerecord-deprecated_finders (1.0.3)
32 | activesupport (4.0.4)
33 | i18n (~> 0.6, >= 0.6.9)
34 | minitest (~> 4.2)
35 | multi_json (~> 1.3)
36 | thread_safe (~> 0.1)
37 | tzinfo (~> 0.3.37)
38 | addressable (2.3.6)
39 | arel (4.0.2)
40 | braintree (2.36.0)
41 | builder (>= 2.0.0)
42 | builder (3.1.4)
43 | capybara (2.2.1)
44 | mime-types (>= 1.16)
45 | nokogiri (>= 1.3.3)
46 | rack (>= 1.0.0)
47 | rack-test (>= 0.5.4)
48 | xpath (~> 2.0)
49 | carmen (1.0.1)
50 | unicode_utils (~> 1.4.0)
51 | carmen-rails (1.0.1)
52 | carmen (~> 1.0.0)
53 | rails
54 | childprocess (0.5.2)
55 | ffi (~> 1.0, >= 1.0.11)
56 | coffee-rails (4.0.1)
57 | coffee-script (>= 2.2.0)
58 | railties (>= 4.0.0, < 5.0)
59 | coffee-script (2.2.0)
60 | coffee-script-source
61 | execjs
62 | coffee-script-source (1.7.0)
63 | commonjs (0.2.7)
64 | daemons (1.1.9)
65 | diff-lcs (1.2.5)
66 | erubis (2.7.0)
67 | eventmachine (1.0.3)
68 | execjs (2.0.2)
69 | ffi (1.9.3)
70 | haml (4.0.5)
71 | tilt
72 | haml-rails (0.5.3)
73 | actionpack (>= 4.0.1)
74 | activesupport (>= 4.0.1)
75 | haml (>= 3.1, < 5.0)
76 | railties (>= 4.0.1)
77 | hike (1.2.3)
78 | i18n (0.6.11)
79 | jquery-rails (3.1.0)
80 | railties (>= 3.0, < 5.0)
81 | thor (>= 0.14, < 2.0)
82 | json (1.8.1)
83 | launchy (2.4.2)
84 | addressable (~> 2.3)
85 | less (2.5.0)
86 | commonjs (~> 0.2.7)
87 | less-rails (2.5.0)
88 | actionpack (>= 3.1)
89 | less (~> 2.5.0)
90 | libv8 (3.16.14.3)
91 | mail (2.5.4)
92 | mime-types (~> 1.16)
93 | treetop (~> 1.4.8)
94 | mime-types (1.25.1)
95 | mini_portile (0.5.3)
96 | minitest (4.7.5)
97 | multi_json (1.10.1)
98 | newrelic_rpm (3.7.3.204)
99 | nokogiri (1.6.1)
100 | mini_portile (~> 0.5.0)
101 | pg (0.17.1)
102 | polyglot (0.3.4)
103 | quiet_assets (1.0.2)
104 | railties (>= 3.1, < 5.0)
105 | rack (1.5.2)
106 | rack-test (0.6.2)
107 | rack (>= 1.0)
108 | rails (4.0.4)
109 | actionmailer (= 4.0.4)
110 | actionpack (= 4.0.4)
111 | activerecord (= 4.0.4)
112 | activesupport (= 4.0.4)
113 | bundler (>= 1.3.0, < 2.0)
114 | railties (= 4.0.4)
115 | sprockets-rails (~> 2.0.0)
116 | rails_on_heroku (0.0.2)
117 | rails_serve_static_assets
118 | rails_stdout_logging
119 | rails_serve_static_assets (0.0.2)
120 | rails_stdout_logging (0.0.3)
121 | railties (4.0.4)
122 | actionpack (= 4.0.4)
123 | activesupport (= 4.0.4)
124 | rake (>= 0.8.7)
125 | thor (>= 0.18.1, < 2.0)
126 | rake (10.3.1)
127 | ref (1.0.5)
128 | rspec-core (2.14.8)
129 | rspec-expectations (2.14.5)
130 | diff-lcs (>= 1.1.3, < 2.0)
131 | rspec-mocks (2.14.6)
132 | rspec-rails (2.14.2)
133 | actionpack (>= 3.0)
134 | activemodel (>= 3.0)
135 | activesupport (>= 3.0)
136 | railties (>= 3.0)
137 | rspec-core (~> 2.14.0)
138 | rspec-expectations (~> 2.14.0)
139 | rspec-mocks (~> 2.14.0)
140 | rubyzip (1.1.3)
141 | selenium-webdriver (2.41.0)
142 | childprocess (>= 0.5.0)
143 | multi_json (~> 1.0)
144 | rubyzip (~> 1.0)
145 | websocket (~> 1.0.4)
146 | shoulda-matchers (2.6.0)
147 | activesupport (>= 3.0.0)
148 | simple_form (3.0.2)
149 | actionpack (~> 4.0)
150 | activemodel (~> 4.0)
151 | sprockets (2.12.1)
152 | hike (~> 1.2)
153 | multi_json (~> 1.0)
154 | rack (~> 1.0)
155 | tilt (~> 1.1, != 1.3.0)
156 | sprockets-rails (2.0.1)
157 | actionpack (>= 3.0)
158 | activesupport (>= 3.0)
159 | sprockets (~> 2.8)
160 | sqlite3 (1.3.9)
161 | therubyracer (0.12.1)
162 | libv8 (~> 3.16.14.0)
163 | ref
164 | thin (1.6.2)
165 | daemons (>= 1.0.9)
166 | eventmachine (>= 1.0.0)
167 | rack (>= 1.0.0)
168 | thor (0.19.1)
169 | thread_safe (0.3.4)
170 | tilt (1.4.1)
171 | treetop (1.4.15)
172 | polyglot
173 | polyglot (>= 0.3.1)
174 | twitter-bootstrap-rails (2.2.8)
175 | actionpack (>= 3.1)
176 | execjs
177 | rails (>= 3.1)
178 | railties (>= 3.1)
179 | tzinfo (0.3.42)
180 | uglifier (2.5.0)
181 | execjs (>= 0.3.0)
182 | json (>= 1.8.0)
183 | unicode_utils (1.4.0)
184 | websocket (1.0.7)
185 | xpath (2.0.0)
186 | nokogiri (~> 1.3)
187 |
188 | PLATFORMS
189 | ruby
190 |
191 | DEPENDENCIES
192 | braintree-rails!
193 | capybara
194 | carmen-rails
195 | coffee-rails (~> 4.0.1)
196 | haml-rails
197 | jquery-rails
198 | launchy
199 | less-rails
200 | newrelic_rpm
201 | pg
202 | quiet_assets
203 | rails (~> 4.0.3)
204 | rails_on_heroku
205 | rspec-rails (~> 2.14.2)
206 | selenium-webdriver
207 | shoulda-matchers
208 | simple_form
209 | sqlite3
210 | therubyracer
211 | thin
212 | twitter-bootstrap-rails
213 | uglifier (>= 2.5.0)
214 |
--------------------------------------------------------------------------------
/config/initializers/simple_form.rb:
--------------------------------------------------------------------------------
1 | # Use this setup block to configure all options available in SimpleForm.
2 | SimpleForm.setup do |config|
3 | # Wrappers are used by the form builder to generate a
4 | # complete input. You can remove any component from the
5 | # wrapper, change the order or even add your own to the
6 | # stack. The options given below are used to wrap the
7 | # whole input.
8 | config.wrappers :default, class: :input, hint_class: :field_with_hint, error_class: :field_with_errors do |b|
9 | ## Extensions enabled by default
10 | # Any of these extensions can be disabled for a
11 | # given input by passing `f.input EXTENSION_NAME: false`.
12 | # You can make any of these extensions optional by
13 | # renaming `b.use` to `b.optional`.
14 |
15 | # Determines whether to use HTML5 (:email, :url, ...)
16 | # and required attributes
17 | b.use :html5
18 |
19 | # Calculates placeholders automatically from I18n
20 | # You can also pass a string as f.input placeholder: "Placeholder"
21 | b.use :placeholder
22 |
23 | ## Optional extensions
24 | # They are disabled unless you pass `f.input EXTENSION_NAME => :lookup`
25 | # to the input. If so, they will retrieve the values from the model
26 | # if any exists. If you want to enable the lookup for any of those
27 | # extensions by default, you can change `b.optional` to `b.use`.
28 |
29 | # Calculates maxlength from length validations for string inputs
30 | b.optional :maxlength
31 |
32 | # Calculates pattern from format validations for string inputs
33 | b.optional :pattern
34 |
35 | # Calculates min and max from length validations for numeric inputs
36 | b.optional :min_max
37 |
38 | # Calculates readonly automatically from readonly attributes
39 | b.optional :readonly
40 |
41 | ## Inputs
42 | b.use :label_input
43 | b.use :hint, wrap_with: { tag: :span, class: :hint }
44 | b.use :error, wrap_with: { tag: :span, class: :error }
45 | end
46 |
47 | config.wrappers :bootstrap, tag: 'div', class: 'control-group', error_class: 'error' do |b|
48 | b.use :html5
49 | b.use :placeholder
50 | b.use :label
51 | b.wrapper tag: 'div', class: 'controls' do |ba|
52 | ba.use :input
53 | ba.use :error, wrap_with: { tag: 'span', class: 'help-inline' }
54 | ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
55 | end
56 | end
57 |
58 | config.wrappers :prepend, tag: 'div', class: "control-group", error_class: 'error' do |b|
59 | b.use :html5
60 | b.use :placeholder
61 | b.use :label
62 | b.wrapper tag: 'div', class: 'controls' do |input|
63 | input.wrapper tag: 'div', class: 'input-prepend' do |prepend|
64 | prepend.use :input
65 | end
66 | input.use :hint, wrap_with: { tag: 'span', class: 'help-block' }
67 | input.use :error, wrap_with: { tag: 'span', class: 'help-inline' }
68 | end
69 | end
70 |
71 | config.wrappers :append, tag: 'div', class: "control-group", error_class: 'error' do |b|
72 | b.use :html5
73 | b.use :placeholder
74 | b.use :label
75 | b.wrapper tag: 'div', class: 'controls' do |input|
76 | input.wrapper tag: 'div', class: 'input-append' do |append|
77 | append.use :input
78 | end
79 | input.use :hint, wrap_with: { tag: 'span', class: 'help-block' }
80 | input.use :error, wrap_with: { tag: 'span', class: 'help-inline' }
81 | end
82 | end
83 |
84 | # Wrappers for forms and inputs using the Twitter Bootstrap toolkit.
85 | # Check the Bootstrap docs (http://twitter.github.com/bootstrap)
86 | # to learn about the different styles for forms and inputs,
87 | # buttons and other elements.
88 | config.default_wrapper = :bootstrap
89 |
90 | # Define the way to render check boxes / radio buttons with labels.
91 | # Defaults to :nested for bootstrap config.
92 | # inline: input + label
93 | # nested: label > input
94 | config.boolean_style = :nested
95 |
96 | # Default class for buttons
97 | config.button_class = 'btn'
98 |
99 | # Method used to tidy up errors. Specify any Rails Array method.
100 | # :first lists the first message for each field.
101 | # Use :to_sentence to list all errors for each field.
102 | # config.error_method = :first
103 |
104 | # Default tag used for error notification helper.
105 | config.error_notification_tag = :div
106 |
107 | # CSS class to add for error notification helper.
108 | config.error_notification_class = 'alert alert-error'
109 |
110 | # ID to add for error notification helper.
111 | # config.error_notification_id = nil
112 |
113 | # Series of attempts to detect a default label method for collection.
114 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
115 |
116 | # Series of attempts to detect a default value method for collection.
117 | # config.collection_value_methods = [ :id, :to_s ]
118 |
119 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
120 | # config.collection_wrapper_tag = nil
121 |
122 | # You can define the class to use on all collection wrappers. Defaulting to none.
123 | # config.collection_wrapper_class = nil
124 |
125 | # You can wrap each item in a collection of radio/check boxes with a tag,
126 | # defaulting to :span. Please note that when using :boolean_style = :nested,
127 | # SimpleForm will force this option to be a label.
128 | # config.item_wrapper_tag = :span
129 |
130 | # You can define a class to use in all item wrappers. Defaulting to none.
131 | # config.item_wrapper_class = nil
132 |
133 | # How the label text should be generated altogether with the required text.
134 | # config.label_text = lambda { |label, required| "#{required} #{label}" }
135 |
136 | # You can define the class to use on all labels. Default is nil.
137 | config.label_class = 'control-label'
138 |
139 | # You can define the class to use on all forms. Default is simple_form.
140 | # config.form_class = :simple_form
141 |
142 | # You can define which elements should obtain additional classes
143 | # config.generate_additional_classes_for = [:wrapper, :label, :input]
144 |
145 | # Whether attributes are required by default (or not). Default is true.
146 | # config.required_by_default = true
147 |
148 | # Tell browsers whether to use default HTML5 validations (novalidate option).
149 | # Default is enabled.
150 | config.browser_validations = false
151 |
152 | # Collection of methods to detect if a file type was given.
153 | # config.file_methods = [ :mounted_as, :file?, :public_filename ]
154 |
155 | # Custom mappings for input types. This should be a hash containing a regexp
156 | # to match as key, and the input type that will be used when the field name
157 | # matches the regexp as value.
158 | # config.input_mappings = { /count/ => :integer }
159 |
160 | # Default priority for time_zone inputs.
161 | # config.time_zone_priority = nil
162 |
163 | # Default priority for country inputs.
164 | # config.country_priority = nil
165 |
166 | # Default size for text inputs.
167 | # config.default_input_size = 50
168 |
169 | # When false, do not use translations for labels.
170 | # config.translate_labels = true
171 |
172 | # Automatically discover new inputs in Rails' autoload path.
173 | # config.inputs_discovery = true
174 |
175 | # Cache SimpleForm inputs discovery
176 | # config.cache_discovery = !Rails.env.development?
177 | end
178 |
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | == Welcome to Rails
2 |
3 | Rails is a web-application framework that includes everything needed to create
4 | database-backed web applications according to the Model-View-Control pattern.
5 |
6 | This pattern splits the view (also called the presentation) into "dumb"
7 | templates that are primarily responsible for inserting pre-built data in between
8 | HTML tags. The model contains the "smart" domain objects (such as Account,
9 | Product, Person, Post) that holds all the business logic and knows how to
10 | persist themselves to a database. The controller handles the incoming requests
11 | (such as Save New Account, Update Product, Show Post) by manipulating the model
12 | and directing data to the view.
13 |
14 | In Rails, the model is handled by what's called an object-relational mapping
15 | layer entitled Active Record. This layer allows you to present the data from
16 | database rows as objects and embellish these data objects with business logic
17 | methods. You can read more about Active Record in
18 | link:files/vendor/rails/activerecord/README.html.
19 |
20 | The controller and view are handled by the Action Pack, which handles both
21 | layers by its two parts: Action View and Action Controller. These two layers
22 | are bundled in a single package due to their heavy interdependence. This is
23 | unlike the relationship between the Active Record and Action Pack that is much
24 | more separate. Each of these packages can be used independently outside of
25 | Rails. You can read more about Action Pack in
26 | link:files/vendor/rails/actionpack/README.html.
27 |
28 |
29 | == Getting Started
30 |
31 | 1. At the command prompt, create a new Rails application:
32 | rails new myapp (where myapp is the application name)
33 |
34 | 2. Change directory to myapp and start the web server:
35 | cd myapp; rails server (run with --help for options)
36 |
37 | 3. Go to http://localhost:3000/ and you'll see:
38 | "Welcome aboard: You're riding Ruby on Rails!"
39 |
40 | 4. Follow the guidelines to start developing your application. You can find
41 | the following resources handy:
42 |
43 | * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
44 | * Ruby on Rails Tutorial Book: http://www.railstutorial.org/
45 |
46 |
47 | == Debugging Rails
48 |
49 | Sometimes your application goes wrong. Fortunately there are a lot of tools that
50 | will help you debug it and get it back on the rails.
51 |
52 | First area to check is the application log files. Have "tail -f" commands
53 | running on the server.log and development.log. Rails will automatically display
54 | debugging and runtime information to these files. Debugging info will also be
55 | shown in the browser on requests from 127.0.0.1.
56 |
57 | You can also log your own messages directly into the log file from your code
58 | using the Ruby logger class from inside your controllers. Example:
59 |
60 | class WeblogController < ActionController::Base
61 | def destroy
62 | @weblog = Weblog.find(params[:id])
63 | @weblog.destroy
64 | logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
65 | end
66 | end
67 |
68 | The result will be a message in your log file along the lines of:
69 |
70 | Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
71 |
72 | More information on how to use the logger is at http://www.ruby-doc.org/core/
73 |
74 | Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
75 | several books available online as well:
76 |
77 | * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
78 | * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
79 |
80 | These two books will bring you up to speed on the Ruby language and also on
81 | programming in general.
82 |
83 |
84 | == Debugger
85 |
86 | Debugger support is available through the debugger command when you start your
87 | Mongrel or WEBrick server with --debugger. This means that you can break out of
88 | execution at any point in the code, investigate and change the model, and then,
89 | resume execution! You need to install ruby-debug to run the server in debugging
90 | mode. With gems, use sudo gem install ruby-debug. Example:
91 |
92 | class WeblogController < ActionController::Base
93 | def index
94 | @posts = Post.all
95 | debugger
96 | end
97 | end
98 |
99 | So the controller will accept the action, run the first line, then present you
100 | with a IRB prompt in the server window. Here you can do things like:
101 |
102 | >> @posts.inspect
103 | => "[#nil, "body"=>nil, "id"=>"1"}>,
105 | #"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
107 | >> @posts.first.title = "hello from a debugger"
108 | => "hello from a debugger"
109 |
110 | ...and even better, you can examine how your runtime objects actually work:
111 |
112 | >> f = @posts.first
113 | => #nil, "body"=>nil, "id"=>"1"}>
114 | >> f.
115 | Display all 152 possibilities? (y or n)
116 |
117 | Finally, when you're ready to resume execution, you can enter "cont".
118 |
119 |
120 | == Console
121 |
122 | The console is a Ruby shell, which allows you to interact with your
123 | application's domain model. Here you'll have all parts of the application
124 | configured, just like it is when the application is running. You can inspect
125 | domain models, change values, and save to the database. Starting the script
126 | without arguments will launch it in the development environment.
127 |
128 | To start the console, run rails console from the application
129 | directory.
130 |
131 | Options:
132 |
133 | * Passing the -s, --sandbox argument will rollback any modifications
134 | made to the database.
135 | * Passing an environment name as an argument will load the corresponding
136 | environment. Example: rails console production.
137 |
138 | To reload your controllers and models after launching the console run
139 | reload!
140 |
141 | More information about irb can be found at:
142 | link:http://www.rubycentral.org/pickaxe/irb.html
143 |
144 |
145 | == dbconsole
146 |
147 | You can go to the command line of your database directly through rails
148 | dbconsole. You would be connected to the database with the credentials
149 | defined in database.yml. Starting the script without arguments will connect you
150 | to the development database. Passing an argument will connect you to a different
151 | database, like rails dbconsole production. Currently works for MySQL,
152 | PostgreSQL and SQLite 3.
153 |
154 | == Description of Contents
155 |
156 | The default directory structure of a generated Ruby on Rails application:
157 |
158 | |-- app
159 | | |-- assets
160 | | |-- images
161 | | |-- javascripts
162 | | `-- stylesheets
163 | | |-- controllers
164 | | |-- helpers
165 | | |-- mailers
166 | | |-- models
167 | | `-- views
168 | | `-- layouts
169 | |-- config
170 | | |-- environments
171 | | |-- initializers
172 | | `-- locales
173 | |-- db
174 | |-- doc
175 | |-- lib
176 | | `-- tasks
177 | |-- log
178 | |-- public
179 | |-- script
180 | |-- test
181 | | |-- fixtures
182 | | |-- functional
183 | | |-- integration
184 | | |-- performance
185 | | `-- unit
186 | |-- tmp
187 | | |-- cache
188 | | |-- pids
189 | | |-- sessions
190 | | `-- sockets
191 | `-- vendor
192 | |-- assets
193 | `-- stylesheets
194 | `-- plugins
195 |
196 | app
197 | Holds all the code that's specific to this particular application.
198 |
199 | app/assets
200 | Contains subdirectories for images, stylesheets, and JavaScript files.
201 |
202 | app/controllers
203 | Holds controllers that should be named like weblogs_controller.rb for
204 | automated URL mapping. All controllers should descend from
205 | ApplicationController which itself descends from ActionController::Base.
206 |
207 | app/models
208 | Holds models that should be named like post.rb. Models descend from
209 | ActiveRecord::Base by default.
210 |
211 | app/views
212 | Holds the template files for the view that should be named like
213 | weblogs/index.html.erb for the WeblogsController#index action. All views use
214 | eRuby syntax by default.
215 |
216 | app/views/layouts
217 | Holds the template files for layouts to be used with views. This models the
218 | common header/footer method of wrapping views. In your views, define a layout
219 | using the layout :default and create a file named default.html.erb.
220 | Inside default.html.erb, call <% yield %> to render the view using this
221 | layout.
222 |
223 | app/helpers
224 | Holds view helpers that should be named like weblogs_helper.rb. These are
225 | generated for you automatically when using generators for controllers.
226 | Helpers can be used to wrap functionality for your views into methods.
227 |
228 | config
229 | Configuration files for the Rails environment, the routing map, the database,
230 | and other dependencies.
231 |
232 | db
233 | Contains the database schema in schema.rb. db/migrate contains all the
234 | sequence of Migrations for your schema.
235 |
236 | doc
237 | This directory is where your application documentation will be stored when
238 | generated using rake doc:app
239 |
240 | lib
241 | Application specific libraries. Basically, any kind of custom code that
242 | doesn't belong under controllers, models, or helpers. This directory is in
243 | the load path.
244 |
245 | public
246 | The directory available for the web server. Also contains the dispatchers and the
247 | default HTML files. This should be set as the DOCUMENT_ROOT of your web
248 | server.
249 |
250 | script
251 | Helper scripts for automation and generation.
252 |
253 | test
254 | Unit and functional tests along with fixtures. When using the rails generate
255 | command, template test files will be generated for you and placed in this
256 | directory.
257 |
258 | vendor
259 | External libraries that the application depends on. Also includes the plugins
260 | subdirectory. If the app has frozen rails, those gems also go here, under
261 | vendor/rails/. This directory is in the load path.
262 |
--------------------------------------------------------------------------------
/config/newrelic.yml:
--------------------------------------------------------------------------------
1 | # Here are the settings that are common to all environments
2 | common: &default_settings
3 | # ============================== LICENSE KEY ===============================
4 |
5 | # You must specify the license key associated with your New Relic
6 | # account. This key binds your Agent's data to your account in the
7 | # New Relic service.
8 | license_key: '<%= ENV["NEW_RELIC_LICENSE_KEY"] %>'
9 |
10 | # Agent Enabled (Rails Only)
11 | # Use this setting to force the agent to run or not run.
12 | # Default is 'auto' which means the agent will install and run only
13 | # if a valid dispatcher such as Mongrel is running. This prevents
14 | # it from running with Rake or the console. Set to false to
15 | # completely turn the agent off regardless of the other settings.
16 | # Valid values are true, false and auto.
17 | #
18 | # agent_enabled: auto
19 |
20 | # Application Name Set this to be the name of your application as
21 | # you'd like it show up in New Relic. The service will then auto-map
22 | # instances of your application into an "application" on your
23 | # dashboard page. If you want to map this instance into multiple
24 | # apps, like "AJAX Requests" and "All UI" then specify a semicolon
25 | # separated list of up to three distinct names, or a yaml list.
26 | # Defaults to the capitalized RAILS_ENV or RACK_ENV (i.e.,
27 | # Production, Staging, etc)
28 | #
29 | # Example:
30 | #
31 | # app_name:
32 | # - Ajax Service
33 | # - All Services
34 | #
35 | app_name: <%= ENV["NEW_RELIC_APP_NAME"] %>
36 |
37 | # When "true", the agent collects performance data about your
38 | # application and reports this data to the New Relic service at
39 | # newrelic.com. This global switch is normally overridden for each
40 | # environment below. (formerly called 'enabled')
41 | monitor_mode: true
42 |
43 | # Developer mode should be off in every environment but
44 | # development as it has very high overhead in memory.
45 | developer_mode: false
46 |
47 | # The newrelic agent generates its own log file to keep its logging
48 | # information separate from that of your application. Specify its
49 | # log level here.
50 | log_level: info
51 |
52 | # Optionally set the path to the log file This is expanded from the
53 | # root directory (may be relative or absolute, e.g. 'log/' or
54 | # '/var/log/') The agent will attempt to create this directory if it
55 | # does not exist.
56 | # log_file_path: 'log'
57 |
58 | # Optionally set the name of the log file, defaults to 'newrelic_agent.log'
59 | # log_file_name: 'newrelic_agent.log'
60 |
61 | # The newrelic agent communicates with the service via http by
62 | # default. If you want to communicate via https to increase
63 | # security, then turn on SSL by setting this value to true. Note,
64 | # this will result in increased CPU overhead to perform the
65 | # encryption involved in SSL communication, but this work is done
66 | # asynchronously to the threads that process your application code,
67 | # so it should not impact response times.
68 | ssl: true
69 |
70 | # EXPERIMENTAL: enable verification of the SSL certificate sent by
71 | # the server. This setting has no effect unless SSL is enabled
72 | # above. This may block your application. Only enable it if the data
73 | # you send us needs end-to-end verified certificates.
74 | #
75 | # This means we cannot cache the DNS lookup, so each request to the
76 | # service will perform a lookup. It also means that we cannot
77 | # use a non-blocking lookup, so in a worst case, if you have DNS
78 | # problems, your app may block indefinitely.
79 | # verify_certificate: true
80 |
81 | # Set your application's Apdex threshold value with the 'apdex_t'
82 | # setting, in seconds. The apdex_t value determines the buckets used
83 | # to compute your overall Apdex score.
84 | # Requests that take less than apdex_t seconds to process will be
85 | # classified as Satisfying transactions; more than apdex_t seconds
86 | # as Tolerating transactions; and more than four times the apdex_t
87 | # value as Frustrating transactions.
88 | # For more about the Apdex standard, see
89 | # http://newrelic.com/docs/general/apdex
90 |
91 | apdex_t: 0.5
92 |
93 | #============================== Browser Monitoring ===============================
94 | # New Relic Real User Monitoring gives you insight into the performance real users are
95 | # experiencing with your website. This is accomplished by measuring the time it takes for
96 | # your users' browsers to download and render your web pages by injecting a small amount
97 | # of JavaScript code into the header and footer of each page.
98 | browser_monitoring:
99 | # By default the agent automatically injects the monitoring JavaScript
100 | # into web pages. Set this attribute to false to turn off this behavior.
101 | auto_instrument: true
102 |
103 | # Proxy settings for connecting to the service.
104 | #
105 | # If a proxy is used, the host setting is required. Other settings
106 | # are optional. Default port is 8080.
107 | #
108 | # proxy_host: hostname
109 | # proxy_port: 8080
110 | # proxy_user:
111 | # proxy_pass:
112 |
113 |
114 | # Tells transaction tracer and error collector (when enabled)
115 | # whether or not to capture HTTP params. When true, frameworks can
116 | # exclude HTTP parameters from being captured.
117 | # Rails: the RoR filter_parameter_logging excludes parameters
118 | # Java: create a config setting called "ignored_params" and set it to
119 | # a comma separated list of HTTP parameter names.
120 | # ex: ignored_params: credit_card, ssn, password
121 | capture_params: false
122 |
123 |
124 | # Transaction tracer captures deep information about slow
125 | # transactions and sends this to the service once a
126 | # minute. Included in the transaction is the exact call sequence of
127 | # the transactions including any SQL statements issued.
128 | transaction_tracer:
129 |
130 | # Transaction tracer is enabled by default. Set this to false to
131 | # turn it off. This feature is only available at the Professional
132 | # and above product levels.
133 | enabled: true
134 |
135 | # Threshold in seconds for when to collect a transaction
136 | # trace. When the response time of a controller action exceeds
137 | # this threshold, a transaction trace will be recorded and sent to
138 | # the service. Valid values are any float value, or (default)
139 | # "apdex_f", which will use the threshold for an dissatisfying
140 | # Apdex controller action - four times the Apdex T value.
141 | transaction_threshold: apdex_f
142 |
143 | # When transaction tracer is on, SQL statements can optionally be
144 | # recorded. The recorder has three modes, "off" which sends no
145 | # SQL, "raw" which sends the SQL statement in its original form,
146 | # and "obfuscated", which strips out numeric and string literals
147 | record_sql: obfuscated
148 |
149 | # Threshold in seconds for when to collect stack trace for a SQL
150 | # call. In other words, when SQL statements exceed this threshold,
151 | # then capture and send the current stack trace. This is
152 | # helpful for pinpointing where long SQL calls originate from
153 | stack_trace_threshold: 0.500
154 |
155 | # Determines whether the agent will capture query plans for slow
156 | # SQL queries. Only supported in mysql and postgres. Should be
157 | # set to false when using other adapters.
158 | # explain_enabled: true
159 |
160 | # Threshold for query execution time below which query plans will not
161 | # not be captured. Relevant only when `explain_enabled` is true.
162 | # explain_threshold: 0.5
163 |
164 | # Error collector captures information about uncaught exceptions and
165 | # sends them to the service for viewing
166 | error_collector:
167 |
168 | # Error collector is enabled by default. Set this to false to turn
169 | # it off. This feature is only available at the Professional and above
170 | # product levels
171 | enabled: true
172 |
173 | # Rails Only - tells error collector whether or not to capture a
174 | # source snippet around the place of the error when errors are View
175 | # related.
176 | capture_source: true
177 |
178 | # To stop specific errors from reporting to New Relic, set this property
179 | # to comma separated values. Default is to ignore routing errors
180 | # which are how 404's get triggered.
181 | #
182 | ignore_errors: ActionController::RoutingError
183 |
184 | # (Advanced) Uncomment this to ensure the cpu and memory samplers
185 | # won't run. Useful when you are using the agent to monitor an
186 | # external resource
187 | # disable_samplers: true
188 |
189 | # If you aren't interested in visibility in these areas, you can
190 | # disable the instrumentation to reduce overhead.
191 | #
192 | # disable_view_instrumentation: true
193 | # disable_activerecord_instrumentation: true
194 | # disable_memcache_instrumentation: true
195 | # disable_dj: true
196 |
197 | # If you're interested in capturing memcache keys as though they
198 | # were SQL uncomment this flag. Note that this does increase
199 | # overhead slightly on every memcached call, and can have security
200 | # implications if your memcached keys are sensitive
201 | # capture_memcache_keys: true
202 |
203 | # Certain types of instrumentation such as GC stats will not work if
204 | # you are running multi-threaded. Please let us know.
205 | # multi_threaded = false
206 |
207 | # Application Environments
208 | # ------------------------------------------
209 | # Environment specific settings are in this section.
210 | # For Rails applications, RAILS_ENV is used to determine the environment
211 | # For Java applications, pass -Dnewrelic.environment to set
212 | # the environment
213 |
214 | # NOTE if your application has other named environments, you should
215 | # provide newrelic configuration settings for these environments here.
216 |
217 | development:
218 | <<: *default_settings
219 | # Turn off communication to New Relic service in development mode (also
220 | # 'enabled').
221 | # NOTE: for initial evaluation purposes, you may want to temporarily
222 | # turn the agent on in development mode.
223 | monitor_mode: false
224 |
225 | # Rails Only - when running in Developer Mode, the New Relic Agent will
226 | # present performance information on the last 100 transactions you have
227 | # executed since starting the mongrel.
228 | # NOTE: There is substantial overhead when running in developer mode.
229 | # Do not use for production or load testing.
230 | developer_mode: true
231 |
232 | # Enable textmate links
233 | # textmate: true
234 |
235 | test:
236 | <<: *default_settings
237 | # It almost never makes sense to turn on the agent when running
238 | # unit, functional or integration tests or the like.
239 | monitor_mode: false
240 |
241 | # Turn on the agent in production for 24x7 monitoring. NewRelic
242 | # testing shows an average performance impact of < 5 ms per
243 | # transaction, you you can leave this on all the time without
244 | # incurring any user-visible performance degradation.
245 | production:
246 | <<: *default_settings
247 | monitor_mode: true
248 |
249 | # Many applications have a staging environment which behaves
250 | # identically to production. Support for that environment is provided
251 | # here. By default, the staging environment has the agent turned on.
252 | staging:
253 | <<: *default_settings
254 | monitor_mode: true
255 | app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> (Staging)
256 |
--------------------------------------------------------------------------------
/app/assets/javascripts/braintree-data.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Braintree Data
3 | * https://www.braintreepayments.com
4 | * Copyright (c) 2009-2013 Braintree Payment Solutions
5 | *
6 | * Licensed under the MIT License.
7 | * http://opensource.org/licenses/MIT
8 | */
9 |
10 | (function(){"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(message){this.toString=function(){return"CORRUPT: "+this.message;};this.message=message;},invalid:function(message){this.toString=function(){return"INVALID: "+this.message;};this.message=message;},bug:function(message){this.toString=function(){return"BUG: "+this.message;};this.message=message;},notReady:function(message){this.toString=function(){return"NOT READY: "+this.message;};this.message=message;}}};if(typeof module!='undefined'&&module.exports){module.exports=sjcl;}
11 | sjcl.bitArray={bitSlice:function(a,bstart,bend){a=sjcl.bitArray._shiftRight(a.slice(bstart/32),32-(bstart&31)).slice(1);return(bend===undefined)?a:sjcl.bitArray.clamp(a,bend-bstart);},extract:function(a,bstart,blength){var x,sh=Math.floor((-bstart-blength)&31);if((bstart+blength-1^bstart)&-32){x=(a[bstart/32|0]<<(32-sh))^(a[bstart/32+1|0]>>>sh);}else{x=a[bstart/32|0]>>>sh;}
12 | return x&((1<0&&len){a[l-1]=sjcl.bitArray.partial(len,a[l-1]&0x80000000>>(len-1),1);}
16 | return a;},partial:function(len,x,_end){if(len===32){return x;}
17 | return(_end?x|0:x<<(32-len))+len*0x10000000000;},getPartial:function(x){return Math.round(x/0x10000000000)||32;},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b)){return false;}
18 | var x=0,i;for(i=0;i=32;shift-=32){out.push(carry);carry=0;}
21 | if(shift===0){return out.concat(a);}
22 | for(i=0;i>>shift);carry=a[i]<<(32-shift);}
23 | last2=a.length?a[a.length-1]:0;shift2=sjcl.bitArray.getPartial(last2);out.push(sjcl.bitArray.partial(shift+shift2&31,(shift+shift2>32)?carry:out.pop(),1));return out;},_xor4:function(x,y){return[x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];}};sjcl.codec.hex={fromBits:function(arr){var out="",i,x;for(i=0;i>>7^a>>>18^a>>>3^a<<25^a<<14)+
34 | (b>>>17^b>>>19^b>>>10^b<<15^b<<13)+
35 | w[i&15]+w[(i+9)&15])|0;}
36 | tmp=(tmp+h7+(h4>>>6^h4>>>11^h4>>>25^h4<<26^h4<<21^h4<<7)+(h6^h4&(h5^h6))+k[i]);h7=h6;h6=h5;h5=h4;h4=h3+tmp|0;h3=h2;h2=h1;h1=h0;h0=(tmp+((h1&h2)^(h3&(h1^h2)))+(h1>>>2^h1>>>13^h1>>>22^h1<<30^h1<<19^h1<<10))|0;}
37 | h[0]=h[0]+h0|0;h[1]=h[1]+h1|0;h[2]=h[2]+h2|0;h[3]=h[3]+h3|0;h[4]=h[4]+h4|0;h[5]=h[5]+h5|0;h[6]=h[6]+h6|0;h[7]=h[7]+h7|0;}};sjcl.cipher.aes=function(key){if(!this._tables[0][0][0]){this._precompute();}
38 | var i,j,tmp,encKey,decKey,sbox=this._tables[0][4],decTable=this._tables[1],keyLen=key.length,rcon=1;if(keyLen!==4&&keyLen!==6&&keyLen!==8){throw new sjcl.exception.invalid("invalid aes key size");}
39 | this._key=[encKey=key.slice(0),decKey=[]];for(i=keyLen;i<4*keyLen+28;i++){tmp=encKey[i-1];if(i%keyLen===0||(keyLen===8&&i%keyLen===4)){tmp=sbox[tmp>>>24]<<24^sbox[tmp>>16&255]<<16^sbox[tmp>>8&255]<<8^sbox[tmp&255];if(i%keyLen===0){tmp=tmp<<8^tmp>>>24^rcon<<24;rcon=rcon<<1^(rcon>>7)*283;}}
40 | encKey[i]=encKey[i-keyLen]^tmp;}
41 | for(j=0;i;j++,i--){tmp=encKey[j&3?i:i-4];if(i<=4||j<4){decKey[j]=tmp;}else{decKey[j]=decTable[0][sbox[tmp>>>24]]^decTable[1][sbox[tmp>>16&255]]^decTable[2][sbox[tmp>>8&255]]^decTable[3][sbox[tmp&255]];}}};sjcl.cipher.aes.prototype={encrypt:function(data){return this._crypt(data,0);},decrypt:function(data){return this._crypt(data,1);},_tables:[[[],[],[],[],[]],[[],[],[],[],[]]],_precompute:function(){var encTable=this._tables[0],decTable=this._tables[1],sbox=encTable[4],sboxInv=decTable[4],i,x,xInv,d=[],th=[],x2,x4,x8,s,tEnc,tDec;for(i=0;i<256;i++){th[(d[i]=i<<1^(i>>7)*283)^i]=i;}
42 | for(x=xInv=0;!sbox[x];x^=x2||1,xInv=th[xInv]||1){s=xInv^xInv<<1^xInv<<2^xInv<<3^xInv<<4;s=s>>8^s&255^99;sbox[x]=s;sboxInv[s]=x;x8=d[x4=d[x2=d[x]]];tDec=x8*0x1010101^x4*0x10001^x2*0x101^x*0x1010100;tEnc=d[s]*0x101^s*0x1010100;for(i=0;i<4;i++){encTable[i][x]=tEnc=tEnc<<24^tEnc>>>8;decTable[i][s]=tDec=tDec<<24^tDec>>>8;}}
43 | for(i=0;i<5;i++){encTable[i]=encTable[i].slice(0);decTable[i]=decTable[i].slice(0);}},_crypt:function(input,dir){if(input.length!==4){throw new sjcl.exception.invalid("invalid aes block size");}
44 | var key=this._key[dir],a=input[0]^key[0],b=input[dir?3:1]^key[1],c=input[2]^key[2],d=input[dir?1:3]^key[3],a2,b2,c2,nInnerRounds=key.length/4-2,i,kIndex=4,out=[0,0,0,0],table=this._tables[dir],t0=table[0],t1=table[1],t2=table[2],t3=table[3],sbox=table[4];for(i=0;i>>24]^t1[b>>16&255]^t2[c>>8&255]^t3[d&255]^key[kIndex];b2=t0[b>>>24]^t1[c>>16&255]^t2[d>>8&255]^t3[a&255]^key[kIndex+1];c2=t0[c>>>24]^t1[d>>16&255]^t2[a>>8&255]^t3[b&255]^key[kIndex+2];d=t0[d>>>24]^t1[a>>16&255]^t2[b>>8&255]^t3[c&255]^key[kIndex+3];kIndex+=4;a=a2;b=b2;c=c2;}
45 | for(i=0;i<4;i++){out[dir?3&-i:i]=sbox[a>>>24]<<24^sbox[b>>16&255]<<16^sbox[c>>8&255]<<8^sbox[d&255]^key[kIndex++];a2=a;a=b;b=c;c=d;d=a2;}
46 | return out;}};sjcl.random={randomWords:function(nwords,paranoia){var out=[],i,readiness=this.isReady(paranoia),g;if(readiness===this._NOT_READY){throw new sjcl.exception.notReady("generator isn't seeded");}else if(readiness&this._REQUIRES_RESEED){this._reseedFromPools(!(readiness&this._READY));}
47 | for(i=0;i0){estimatedEntropy++;tmp=tmp>>>1;}}}
56 | this._pools[robin].update([id,this._eventId++,2,estimatedEntropy,t,data.length].concat(data));}
57 | break;case"string":if(estimatedEntropy===undefined){estimatedEntropy=data.length;}
58 | this._pools[robin].update([id,this._eventId++,3,estimatedEntropy,t,data.length]);this._pools[robin].update(data);break;default:err=1;}
59 | if(err){throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");}
60 | this._poolEntropy[robin]+=estimatedEntropy;this._poolStrength+=estimatedEntropy;if(oldReady===this._NOT_READY){if(this.isReady()!==this._NOT_READY){this._fireEvent("seeded",Math.max(this._strength,this._poolStrength));}
61 | this._fireEvent("progress",this.getProgress());}},isReady:function(paranoia){var entropyRequired=this._PARANOIA_LEVELS[(paranoia!==undefined)?paranoia:this._defaultParanoia];if(this._strength&&this._strength>=entropyRequired){return(this._poolEntropy[0]>this._BITS_PER_RESEED&&(new Date()).valueOf()>this._nextReseed)?this._REQUIRES_RESEED|this._READY:this._READY;}else{return(this._poolStrength>=entropyRequired)?this._REQUIRES_RESEED|this._NOT_READY:this._NOT_READY;}},getProgress:function(paranoia){var entropyRequired=this._PARANOIA_LEVELS[paranoia?paranoia:this._defaultParanoia];if(this._strength>=entropyRequired){return 1.0;}else{return(this._poolStrength>entropyRequired)?1.0:this._poolStrength/entropyRequired;}},startCollectors:function(){if(this._collectorsStarted){return;}
62 | if(window.addEventListener){window.addEventListener("load",this._loadTimeCollector,false);window.addEventListener("mousemove",this._mouseCollector,false);}else if(document.attachEvent){document.attachEvent("onload",this._loadTimeCollector);document.attachEvent("onmousemove",this._mouseCollector);}
63 | else{throw new sjcl.exception.bug("can't attach event");}
64 | this._collectorsStarted=true;},stopCollectors:function(){if(!this._collectorsStarted){return;}
65 | if(window.removeEventListener){window.removeEventListener("load",this._loadTimeCollector,false);window.removeEventListener("mousemove",this._mouseCollector,false);}else if(window.detachEvent){window.detachEvent("onload",this._loadTimeCollector);window.detachEvent("onmousemove",this._mouseCollector);}
66 | this._collectorsStarted=false;},addEventListener:function(name,callback){this._callbacks[name][this._callbackI++]=callback;},removeEventListener:function(name,cb){var i,j,cbs=this._callbacks[name],jsTemp=[];for(j in cbs){if(cbs.hasOwnProperty(j)&&cbs[j]===cb){jsTemp.push(j);}}
67 | for(i=0;i=1<this._strength){this._strength=strength;}
72 | this._reseedCount++;this._reseed(reseedData);},_mouseCollector:function(ev){var x=ev.x||ev.clientX||ev.offsetX||0,y=ev.y||ev.clientY||ev.offsetY||0;sjcl.random.addEntropy([x,y],2,"mouse");},_loadTimeCollector:function(ev){sjcl.random.addEntropy((new Date()).valueOf(),2,"loadtime");},_fireEvent:function(name,arg){var j,cbs=sjcl.random._callbacks[name],cbsTemp=[];for(j in cbs){if(cbs.hasOwnProperty(j)){cbsTemp.push(cbs[j]);}}
73 | for(j=0;j
14 | * Licensed under the ISC License.
15 | * http://opensource.org/licenses/ISC
16 | */
17 |
18 | Braintree=(function(){function Stream(enc,pos){if(enc instanceof Stream){this.enc=enc.enc;this.pos=enc.pos;}else{this.enc=enc;this.pos=pos;}}
19 | Stream.prototype.get=function(pos){if(pos==undefined)
20 | pos=this.pos++;if(pos>=this.enc.length)
21 | throw'Requesting byte offset '+pos+' on a stream of length '+this.enc.length;return this.enc[pos];}
22 | Stream.prototype.hexDigits="0123456789ABCDEF";Stream.prototype.hexByte=function(b){return this.hexDigits.charAt((b>>4)&0xF)+this.hexDigits.charAt(b&0xF);}
23 | Stream.prototype.hexDump=function(start,end){var s="";for(var i=start;i191)&&(c<224))
29 | s+=String.fromCharCode(((c&0x1F)<<6)|(this.get(i++)&0x3F));else
30 | s+=String.fromCharCode(((c&0x0F)<<12)|((this.get(i++)&0x3F)<<6)|(this.get(i++)&0x3F));}
31 | return s;}
32 | Stream.prototype.reTime=/^((?:1[89]|2\d)?\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/;Stream.prototype.parseTime=function(start,end){var s=this.parseStringISO(start,end);var m=this.reTime.exec(s);if(!m)
33 | return"Unrecognized time: "+s;s=m[1]+"-"+m[2]+"-"+m[3]+" "+m[4];if(m[5]){s+=":"+m[5];if(m[6]){s+=":"+m[6];if(m[7])
34 | s+="."+m[7];}}
35 | if(m[8]){s+=" UTC";if(m[8]!='Z'){s+=m[8];if(m[9])
36 | s+=":"+m[9];}}
37 | return s;}
38 | Stream.prototype.parseInteger=function(start,end){var len=end-start;if(len>4){len<<=3;var s=this.get(start);if(s==0)
39 | len-=8;else
40 | while(s<128){s<<=1;--len;}
41 | return"("+len+" bit)";}
42 | var n=0;for(var i=start;istart;--i){var b=this.get(i);for(var j=skip;j<8;++j)
45 | s+=(b>>j)&1?"1":"0";skip=0;}}
46 | return s;}
47 | Stream.prototype.parseOctetString=function(start,end){var len=end-start;var s="("+len+" byte) ";if(len>20)
48 | end=start+20;for(var i=start;i20)
50 | s+=String.fromCharCode(8230);return s;}
51 | Stream.prototype.parseOID=function(start,end){var s,n=0,bits=0;for(var i=start;i=31)?"bigint":n);n=bits=0;}
54 | s+=String.fromCharCode();}
55 | return s;}
56 | function ASN1(stream,header,length,tag,sub){this.stream=stream;this.header=header;this.length=length;this.tag=tag;this.sub=sub;}
57 | ASN1.prototype.typeName=function(){if(this.tag==undefined)
58 | return"unknown";var tagClass=this.tag>>6;var tagConstructed=(this.tag>>5)&1;var tagNumber=this.tag&0x1F;switch(tagClass){case 0:switch(tagNumber){case 0x00:return"EOC";case 0x01:return"BOOLEAN";case 0x02:return"INTEGER";case 0x03:return"BIT_STRING";case 0x04:return"OCTET_STRING";case 0x05:return"NULL";case 0x06:return"OBJECT_IDENTIFIER";case 0x07:return"ObjectDescriptor";case 0x08:return"EXTERNAL";case 0x09:return"REAL";case 0x0A:return"ENUMERATED";case 0x0B:return"EMBEDDED_PDV";case 0x0C:return"UTF8String";case 0x10:return"SEQUENCE";case 0x11:return"SET";case 0x12:return"NumericString";case 0x13:return"PrintableString";case 0x14:return"TeletexString";case 0x15:return"VideotexString";case 0x16:return"IA5String";case 0x17:return"UTCTime";case 0x18:return"GeneralizedTime";case 0x19:return"GraphicString";case 0x1A:return"VisibleString";case 0x1B:return"GeneralString";case 0x1C:return"UniversalString";case 0x1E:return"BMPString";default:return"Universal_"+tagNumber.toString(16);}
59 | case 1:return"Application_"+tagNumber.toString(16);case 2:return"["+tagNumber+"]";case 3:return"Private_"+tagNumber.toString(16);}}
60 | ASN1.prototype.content=function(){if(this.tag==undefined)
61 | return null;var tagClass=this.tag>>6;if(tagClass!=0)
62 | return(this.sub==null)?null:"("+this.sub.length+")";var tagNumber=this.tag&0x1F;var content=this.posContent();var len=Math.abs(this.length);switch(tagNumber){case 0x01:return(this.stream.get(content)==0)?"false":"true";case 0x02:return this.stream.parseInteger(content,content+len);case 0x03:return this.sub?"("+this.sub.length+" elem)":this.stream.parseBitString(content,content+len)
63 | case 0x04:return this.sub?"("+this.sub.length+" elem)":this.stream.parseOctetString(content,content+len)
64 | case 0x06:return this.stream.parseOID(content,content+len);case 0x10:case 0x11:return"("+this.sub.length+" elem)";case 0x0C:return this.stream.parseStringUTF(content,content+len);case 0x12:case 0x13:case 0x14:case 0x15:case 0x16:case 0x1A:return this.stream.parseStringISO(content,content+len);case 0x17:case 0x18:return this.stream.parseTime(content,content+len);}
65 | return null;}
66 | ASN1.prototype.toString=function(){return this.typeName()+"@"+this.stream.pos+"[header:"+this.header+",length:"+this.length+",sub:"+((this.sub==null)?'null':this.sub.length)+"]";}
67 | ASN1.prototype.print=function(indent){if(indent==undefined)indent='';document.writeln(indent+this);if(this.sub!=null){indent+=' ';for(var i=0,max=this.sub.length;i=0)
70 | s+="+";s+=this.length;if(this.tag&0x20)
71 | s+=" (constructed)";else if(((this.tag==0x03)||(this.tag==0x04))&&(this.sub!=null))
72 | s+=" (encapsulates)";s+="\n";if(this.sub!=null){indent+=' ';for(var i=0,max=this.sub.length;i3)
80 | throw"Length over 24 bits not supported at position "+(stream.pos-1);if(len==0)
81 | return-1;buf=0;for(var i=0;i0x04))
85 | return false;var p=new Stream(stream);if(tag==0x03)p.get();var subTag=p.get();if((subTag>>6)&0x01)
86 | return false;try{var subLength=ASN1.decodeLength(p);return((p.pos-stream.pos)+subLength==len);}catch(exception){return false;}}
87 | ASN1.decode=function(stream){if(!(stream instanceof Stream))
88 | stream=new Stream(stream,0);var streamStart=new Stream(stream);var tag=stream.get();var len=ASN1.decodeLength(stream);var header=stream.pos-streamStart.pos;var sub=null;if(ASN1.hasContent(tag,len,stream)){var start=stream.pos;if(tag==0x03)stream.get();sub=[];if(len>=0){var end=start+len;while(stream.pos>6)+b64map.charAt(c&63);}
95 | if(i+1==h.length){c=parseInt(h.substring(i,i+1),16);ret+=b64map.charAt(c<<2);}
96 | else if(i+2==h.length){c=parseInt(h.substring(i,i+2),16);ret+=b64map.charAt(c>>2)+b64map.charAt((c&3)<<4);}
97 | while((ret.length&3)>0)ret+=b64pad;return ret;}
98 | function b64tohex(s){var ret=""
99 | var i;var k=0;var slop;for(i=0;i>2);slop=v&3;k=1;}
100 | else if(k==1){ret+=int2char((slop<<2)|(v>>4));slop=v&0xf;k=2;}
101 | else if(k==2){ret+=int2char(slop);ret+=int2char(v>>2);slop=v&3;k=3;}
102 | else{ret+=int2char((slop<<2)|(v>>4));ret+=int2char(v&0xf);k=0;}}
103 | if(k==1)
104 | ret+=int2char(slop<<2);return ret;}
105 | function b64toBA(s){var h=b64tohex(s);var i;var a=new Array();for(i=0;2*i=0){var v=x*this[i++]+w[j]+c;c=Math.floor(v/0x4000000);w[j++]=v&0x3ffffff;}
111 | return c;}
112 | function am2(i,x,w,j,c,n){var xl=x&0x7fff,xh=x>>15;while(--n>=0){var l=this[i]&0x7fff;var h=this[i++]>>15;var m=xh*l+h*xl;l=xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);c=(l>>>30)+(m>>>15)+xh*h+(c>>>30);w[j++]=l&0x3fffffff;}
113 | return c;}
114 | function am3(i,x,w,j,c,n){var xl=x&0x3fff,xh=x>>14;while(--n>=0){var l=this[i]&0x3fff;var h=this[i++]>>14;var m=xh*l+h*xl;l=xl*l+((m&0x3fff)<<14)+w[j]+c;c=(l>>28)+(m>>14)+xh*h;w[j++]=l&0xfffffff;}
115 | return c;}
116 | if(j_lm&&(navigator.appName=="Microsoft Internet Explorer")){BigInteger.prototype.am=am2;dbits=30;}
117 | else if(j_lm&&(navigator.appName!="Netscape")){BigInteger.prototype.am=am1;dbits=26;}
118 | else{BigInteger.prototype.am=am3;dbits=28;}
119 | BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<=0;--i)r[i]=this[i];r.t=this.t;r.s=this.s;}
122 | function bnpFromInt(x){this.t=1;this.s=(x<0)?-1:0;if(x>0)this[0]=x;else if(x<-1)this[0]=x+DV;else this.t=0;}
123 | function nbv(i){var r=nbi();r.fromInt(i);return r;}
124 | function bnpFromString(s,b){var k;if(b==16)k=4;else if(b==8)k=3;else if(b==256)k=8;else if(b==2)k=1;else if(b==32)k=5;else if(b==4)k=2;else{this.fromRadix(s,b);return;}
125 | this.t=0;this.s=0;var i=s.length,mi=false,sh=0;while(--i>=0){var x=(k==8)?s[i]&0xff:intAt(s,i);if(x<0){if(s.charAt(i)=="-")mi=true;continue;}
126 | mi=false;if(sh==0)
127 | this[this.t++]=x;else if(sh+k>this.DB){this[this.t-1]|=(x&((1<<(this.DB-sh))-1))<>(this.DB-sh));}
128 | else
129 | this[this.t-1]|=x<=this.DB)sh-=this.DB;}
130 | if(k==8&&(s[0]&0x80)!=0){this.s=-1;if(sh>0)this[this.t-1]|=((1<<(this.DB-sh))-1)<0&&this[this.t-1]==c)--this.t;}
133 | function bnToString(b){if(this.s<0)return"-"+this.negate().toString(b);var k;if(b==16)k=4;else if(b==8)k=3;else if(b==2)k=1;else if(b==32)k=5;else if(b==4)k=2;else return this.toRadix(b);var km=(1<0){if(p>p)>0){m=true;r=int2char(d);}
134 | while(i>=0){if(p>(p+=this.DB-k);}
135 | else{d=(this[i]>>(p-=k))&km;if(p<=0){p+=this.DB;--i;}}
136 | if(d>0)m=true;if(m)r+=int2char(d);}}
137 | return m?r:"0";}
138 | function bnNegate(){var r=nbi();BigInteger.ZERO.subTo(this,r);return r;}
139 | function bnAbs(){return(this.s<0)?this.negate():this;}
140 | function bnCompareTo(a){var r=this.s-a.s;if(r!=0)return r;var i=this.t;r=i-a.t;if(r!=0)return(this.s<0)?-r:r;while(--i>=0)if((r=this[i]-a[i])!=0)return r;return 0;}
141 | function nbits(x){var r=1,t;if((t=x>>>16)!=0){x=t;r+=16;}
142 | if((t=x>>8)!=0){x=t;r+=8;}
143 | if((t=x>>4)!=0){x=t;r+=4;}
144 | if((t=x>>2)!=0){x=t;r+=2;}
145 | if((t=x>>1)!=0){x=t;r+=1;}
146 | return r;}
147 | function bnBitLength(){if(this.t<=0)return 0;return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));}
148 | function bnpDLShiftTo(n,r){var i;for(i=this.t-1;i>=0;--i)r[i+n]=this[i];for(i=n-1;i>=0;--i)r[i]=0;r.t=this.t+n;r.s=this.s;}
149 | function bnpDRShiftTo(n,r){for(var i=n;i=0;--i){r[i+ds+1]=(this[i]>>cbs)|c;c=(this[i]&bm)<=0;--i)r[i]=0;r[ds]=c;r.t=this.t+ds+1;r.s=this.s;r.clamp();}
152 | function bnpRShiftTo(n,r){r.s=this.s;var ds=Math.floor(n/this.DB);if(ds>=this.t){r.t=0;return;}
153 | var bs=n%this.DB;var cbs=this.DB-bs;var bm=(1<>bs;for(var i=ds+1;i>bs;}
154 | if(bs>0)r[this.t-ds-1]|=(this.s&bm)<>=this.DB;}
156 | if(a.t>=this.DB;}
157 | c+=this.s;}
158 | else{c+=this.s;while(i>=this.DB;}
159 | c-=a.s;}
160 | r.s=(c<0)?-1:0;if(c<-1)r[i++]=this.DV+c;else if(c>0)r[i++]=c;r.t=i;r.clamp();}
161 | function bnpMultiplyTo(a,r){var x=this.abs(),y=a.abs();var i=x.t;r.t=i+y.t;while(--i>=0)r[i]=0;for(i=0;i=0)r[i]=0;for(i=0;i=x.DV){r[i+x.t]-=x.DV;r[i+x.t+1]=1;}}
163 | if(r.t>0)r[r.t-1]+=x.am(i,x[i],r,2*i,0,1);r.s=0;r.clamp();}
164 | function bnpDivRemTo(m,q,r){var pm=m.abs();if(pm.t<=0)return;var pt=this.abs();if(pt.t0){pm.lShiftTo(nsh,y);pt.lShiftTo(nsh,r);}
166 | else{pm.copyTo(y);pt.copyTo(r);}
167 | var ys=y.t;var y0=y[ys-1];if(y0==0)return;var yt=y0*(1<1)?y[ys-2]>>this.F2:0);var d1=this.FV/yt,d2=(1<=0){r[r.t++]=1;r.subTo(t,r);}
168 | BigInteger.ONE.dlShiftTo(ys,t);t.subTo(y,y);while(y.t=0){var qd=(r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);if((r[i]+=y.am(0,qd,r,j,0,ys))0)r.rShiftTo(nsh,r);if(ts<0)BigInteger.ZERO.subTo(r,r);}
171 | function bnMod(a){var r=nbi();this.abs().divRemTo(a,null,r);if(this.s<0&&r.compareTo(BigInteger.ZERO)>0)a.subTo(r,r);return r;}
172 | function Classic(m){this.m=m;}
173 | function cConvert(x){if(x.s<0||x.compareTo(this.m)>=0)return x.mod(this.m);else return x;}
174 | function cRevert(x){return x;}
175 | function cReduce(x){x.divRemTo(this.m,null,x);}
176 | function cMulTo(x,y,r){x.multiplyTo(y,r);this.reduce(r);}
177 | function cSqrTo(x,r){x.squareTo(r);this.reduce(r);}
178 | Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1)return 0;var x=this[0];if((x&1)==0)return 0;var y=x&3;y=(y*(2-(x&0xf)*y))&0xf;y=(y*(2-(x&0xff)*y))&0xff;y=(y*(2-(((x&0xffff)*y)&0xffff)))&0xffff;y=(y*(2-x*y%this.DV))%this.DV;return(y>0)?this.DV-y:-y;}
179 | function Montgomery(m){this.m=m;this.mp=m.invDigit();this.mpl=this.mp&0x7fff;this.mph=this.mp>>15;this.um=(1<<(m.DB-15))-1;this.mt2=2*m.t;}
180 | function montConvert(x){var r=nbi();x.abs().dlShiftTo(this.m.t,r);r.divRemTo(this.m,null,r);if(x.s<0&&r.compareTo(BigInteger.ZERO)>0)this.m.subTo(r,r);return r;}
181 | function montRevert(x){var r=nbi();x.copyTo(r);this.reduce(r);return r;}
182 | function montReduce(x){while(x.t<=this.mt2)
183 | x[x.t++]=0;for(var i=0;i>15)*this.mpl)&this.um)<<15))&x.DM;j=i+this.m.t;x[j]+=this.m.am(0,u0,x,i,0,this.m.t);while(x[j]>=x.DV){x[j]-=x.DV;x[++j]++;}}
184 | x.clamp();x.drShiftTo(this.m.t,x);if(x.compareTo(this.m)>=0)x.subTo(this.m,x);}
185 | function montSqrTo(x,r){x.squareTo(r);this.reduce(r);}
186 | function montMulTo(x,y,r){x.multiplyTo(y,r);this.reduce(r);}
187 | Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0;}
188 | function bnpExp(e,z){if(e>0xffffffff||e<1)return BigInteger.ONE;var r=nbi(),r2=nbi(),g=z.convert(this),i=nbits(e)-1;g.copyTo(r);while(--i>=0){z.sqrTo(r,r2);if((e&(1<0)z.mulTo(r2,g,r);else{var t=r;r=r2;r2=t;}}
189 | return z.revert(r);}
190 | function bnModPowInt(e,m){var z;if(e<256||m.isEven())z=new Classic(m);else z=new Montgomery(m);return this.exp(e,z);}
191 | BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);function parseBigInt(str,r){return new BigInteger(str,r);}
192 | function linebrk(s,n){var ret="";var i=0;while(i+n=0&&n>0){var c=s.charCodeAt(i--);if(c<128){ba[--n]=c;}
199 | else if((c>127)&&(c<2048)){ba[--n]=(c&63)|128;ba[--n]=(c>>6)|192;}
200 | else{ba[--n]=(c&63)|128;ba[--n]=((c>>6)&63)|128;ba[--n]=(c>>12)|224;}}
201 | ba[--n]=0;var randomByte=0;var random=0;var shift=0;while(n>2){if(shift==0){random=sjcl.random.randomWords(1,0)[0];}
202 | randomByte=(random>>shift)&0xff;shift=(shift+8)%32;if(randomByte!=0){ba[--n]=randomByte;}}
203 | ba[--n]=2;ba[--n]=0;return new BigInteger(ba);}
204 | function RSAKey(){this.n=null;this.e=0;this.d=null;this.p=null;this.q=null;this.dmp1=null;this.dmq1=null;this.coeff=null;}
205 | function RSASetPublic(N,E){if(N!=null&&E!=null&&N.length>0&&E.length>0){this.n=parseBigInt(N,16);this.e=parseInt(E,16);}
206 | else
207 | alert("Invalid RSA public key");}
208 | function RSADoPublic(x){return x.modPowInt(this.e,this.n);}
209 | function RSAEncrypt(text){var m=pkcs1pad2(text,(this.n.bitLength()+7)>>3);if(m==null)return null;var c=this.doPublic(m);if(c==null)return null;var h=c.toString(16);if((h.length&1)==0)return h;else return"0"+h;}
210 | function RSAEncryptB64(text){var h=this.encrypt(text);if(h)return hex2b64(h);else return null;}
211 | RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;RSAKey.prototype.encrypt_b64=RSAEncryptB64;"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(message){this.toString=function(){return"CORRUPT: "+this.message;};this.message=message;},invalid:function(message){this.toString=function(){return"INVALID: "+this.message;};this.message=message;},bug:function(message){this.toString=function(){return"BUG: "+this.message;};this.message=message;},notReady:function(message){this.toString=function(){return"NOT READY: "+this.message;};this.message=message;}}};if(typeof module!='undefined'&&module.exports){module.exports=sjcl;}
212 | sjcl.cipher.aes=function(key){if(!this._tables[0][0][0]){this._precompute();}
213 | var i,j,tmp,encKey,decKey,sbox=this._tables[0][4],decTable=this._tables[1],keyLen=key.length,rcon=1;if(keyLen!==4&&keyLen!==6&&keyLen!==8){throw new sjcl.exception.invalid("invalid aes key size");}
214 | this._key=[encKey=key.slice(0),decKey=[]];for(i=keyLen;i<4*keyLen+28;i++){tmp=encKey[i-1];if(i%keyLen===0||(keyLen===8&&i%keyLen===4)){tmp=sbox[tmp>>>24]<<24^sbox[tmp>>16&255]<<16^sbox[tmp>>8&255]<<8^sbox[tmp&255];if(i%keyLen===0){tmp=tmp<<8^tmp>>>24^rcon<<24;rcon=rcon<<1^(rcon>>7)*283;}}
215 | encKey[i]=encKey[i-keyLen]^tmp;}
216 | for(j=0;i;j++,i--){tmp=encKey[j&3?i:i-4];if(i<=4||j<4){decKey[j]=tmp;}else{decKey[j]=decTable[0][sbox[tmp>>>24]]^decTable[1][sbox[tmp>>16&255]]^decTable[2][sbox[tmp>>8&255]]^decTable[3][sbox[tmp&255]];}}};sjcl.cipher.aes.prototype={encrypt:function(data){return this._crypt(data,0);},decrypt:function(data){return this._crypt(data,1);},_tables:[[[],[],[],[],[]],[[],[],[],[],[]]],_precompute:function(){var encTable=this._tables[0],decTable=this._tables[1],sbox=encTable[4],sboxInv=decTable[4],i,x,xInv,d=[],th=[],x2,x4,x8,s,tEnc,tDec;for(i=0;i<256;i++){th[(d[i]=i<<1^(i>>7)*283)^i]=i;}
217 | for(x=xInv=0;!sbox[x];x^=x2||1,xInv=th[xInv]||1){s=xInv^xInv<<1^xInv<<2^xInv<<3^xInv<<4;s=s>>8^s&255^99;sbox[x]=s;sboxInv[s]=x;x8=d[x4=d[x2=d[x]]];tDec=x8*0x1010101^x4*0x10001^x2*0x101^x*0x1010100;tEnc=d[s]*0x101^s*0x1010100;for(i=0;i<4;i++){encTable[i][x]=tEnc=tEnc<<24^tEnc>>>8;decTable[i][s]=tDec=tDec<<24^tDec>>>8;}}
218 | for(i=0;i<5;i++){encTable[i]=encTable[i].slice(0);decTable[i]=decTable[i].slice(0);}},_crypt:function(input,dir){if(input.length!==4){throw new sjcl.exception.invalid("invalid aes block size");}
219 | var key=this._key[dir],a=input[0]^key[0],b=input[dir?3:1]^key[1],c=input[2]^key[2],d=input[dir?1:3]^key[3],a2,b2,c2,nInnerRounds=key.length/4-2,i,kIndex=4,out=[0,0,0,0],table=this._tables[dir],t0=table[0],t1=table[1],t2=table[2],t3=table[3],sbox=table[4];for(i=0;i>>24]^t1[b>>16&255]^t2[c>>8&255]^t3[d&255]^key[kIndex];b2=t0[b>>>24]^t1[c>>16&255]^t2[d>>8&255]^t3[a&255]^key[kIndex+1];c2=t0[c>>>24]^t1[d>>16&255]^t2[a>>8&255]^t3[b&255]^key[kIndex+2];d=t0[d>>>24]^t1[a>>16&255]^t2[b>>8&255]^t3[c&255]^key[kIndex+3];kIndex+=4;a=a2;b=b2;c=c2;}
220 | for(i=0;i<4;i++){out[dir?3&-i:i]=sbox[a>>>24]<<24^sbox[b>>16&255]<<16^sbox[c>>8&255]<<8^sbox[d&255]^key[kIndex++];a2=a;a=b;b=c;c=d;d=a2;}
221 | return out;}};sjcl.bitArray={bitSlice:function(a,bstart,bend){a=sjcl.bitArray._shiftRight(a.slice(bstart/32),32-(bstart&31)).slice(1);return(bend===undefined)?a:sjcl.bitArray.clamp(a,bend-bstart);},extract:function(a,bstart,blength){var x,sh=Math.floor((-bstart-blength)&31);if((bstart+blength-1^bstart)&-32){x=(a[bstart/32|0]<<(32-sh))^(a[bstart/32+1|0]>>>sh);}else{x=a[bstart/32|0]>>>sh;}
222 | return x&((1<0&&len){a[l-1]=sjcl.bitArray.partial(len,a[l-1]&0x80000000>>(len-1),1);}
226 | return a;},partial:function(len,x,_end){if(len===32){return x;}
227 | return(_end?x|0:x<<(32-len))+len*0x10000000000;},getPartial:function(x){return Math.round(x/0x10000000000)||32;},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b)){return false;}
228 | var x=0,i;for(i=0;i=32;shift-=32){out.push(carry);carry=0;}
231 | if(shift===0){return out.concat(a);}
232 | for(i=0;i>>shift);carry=a[i]<<(32-shift);}
233 | last2=a.length?a[a.length-1]:0;shift2=sjcl.bitArray.getPartial(last2);out.push(sjcl.bitArray.partial(shift+shift2&31,(shift+shift2>32)?carry:out.pop(),1));return out;},_xor4:function(x,y){return[x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];}};sjcl.codec.hex={fromBits:function(arr){var out="",i,x;for(i=0;i>>24);tmp<<=8;}
237 | return decodeURIComponent(escape(out));},toBits:function(str){str=unescape(encodeURIComponent(str));var out=[],i,tmp=0;for(i=0;i>>bits)>>>26);if(bits<6){ta=arr[i]<<(6-bits);bits+=26;i++;}else{ta<<=6;bits-=6;}}
240 | while((out.length&3)&&!_noEquals){out+="=";}
241 | return out;},toBits:function(str,_url){str=str.replace(/\s|=/g,'');var out=[],i,bits=0,c=sjcl.codec.base64._chars,ta=0,x;if(_url)c=c.substr(0,62)+'-_';for(i=0;i26){bits-=26;out.push(ta^x>>>bits);ta=x<<(32-bits);}else{bits+=6;ta^=x<<(32-bits);}}
243 | if(bits&56){out.push(sjcl.bitArray.partial(bits&56,ta,1));}
244 | return out;}};sjcl.codec.base64url={fromBits:function(arr){return sjcl.codec.base64.fromBits(arr,1,1);},toBits:function(str){return sjcl.codec.base64.toBits(str,1);}};if(sjcl.beware===undefined){sjcl.beware={};}
245 | sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]=function(){sjcl.mode.cbc={name:"cbc",encrypt:function(prp,plaintext,iv,adata){if(adata&&adata.length){throw new sjcl.exception.invalid("cbc can't authenticate data");}
246 | if(sjcl.bitArray.bitLength(iv)!==128){throw new sjcl.exception.invalid("cbc iv must be 128 bits");}
247 | var i,w=sjcl.bitArray,xor=w._xor4,bl=w.bitLength(plaintext),bp=0,output=[];if(bl&7){throw new sjcl.exception.invalid("pkcs#5 padding only works for multiples of a byte");}
248 | for(i=0;bp+128<=bl;i+=4,bp+=128){iv=prp.encrypt(xor(iv,plaintext.slice(i,i+4)));output.splice(i,0,iv[0],iv[1],iv[2],iv[3]);}
249 | bl=(16-((bl>>3)&15))*0x1010101;iv=prp.encrypt(xor(iv,w.concat(plaintext,[bl,bl,bl,bl]).slice(i,i+4)));output.splice(i,0,iv[0],iv[1],iv[2],iv[3]);return output;},decrypt:function(prp,ciphertext,iv,adata){if(adata&&adata.length){throw new sjcl.exception.invalid("cbc can't authenticate data");}
250 | if(sjcl.bitArray.bitLength(iv)!==128){throw new sjcl.exception.invalid("cbc iv must be 128 bits");}
251 | if((sjcl.bitArray.bitLength(ciphertext)&127)||!ciphertext.length){throw new sjcl.exception.corrupt("cbc ciphertext must be a positive multiple of the block size");}
252 | var i,w=sjcl.bitArray,xor=w._xor4,bi,bo,output=[];adata=adata||[];for(i=0;i16){throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");}
254 | bo=bi*0x1010101;if(!w.equal(w.bitSlice([bo,bo,bo,bo],0,bi*8),w.bitSlice(output,output.length*32-bi*8,output.length*32))){throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");}
255 | return w.bitSlice(output,0,output.length*32-bi*8);}};};sjcl.misc.hmac=function(key,Hash){this._hash=Hash=Hash||sjcl.hash.sha256;var exKey=[[],[]],i,bs=Hash.prototype.blockSize/32;this._baseHash=[new Hash(),new Hash()];if(key.length>bs){key=Hash.hash(key);}
256 | for(i=0;i>>7^a>>>18^a>>>3^a<<25^a<<14)+
266 | (b>>>17^b>>>19^b>>>10^b<<15^b<<13)+
267 | w[i&15]+w[(i+9)&15])|0;}
268 | tmp=(tmp+h7+(h4>>>6^h4>>>11^h4>>>25^h4<<26^h4<<21^h4<<7)+(h6^h4&(h5^h6))+k[i]);h7=h6;h6=h5;h5=h4;h4=h3+tmp|0;h3=h2;h2=h1;h1=h0;h0=(tmp+((h1&h2)^(h3&(h1^h2)))+(h1>>>2^h1>>>13^h1>>>22^h1<<30^h1<<19^h1<<10))|0;}
269 | h[0]=h[0]+h0|0;h[1]=h[1]+h1|0;h[2]=h[2]+h2|0;h[3]=h[3]+h3|0;h[4]=h[4]+h4|0;h[5]=h[5]+h5|0;h[6]=h[6]+h6|0;h[7]=h[7]+h7|0;}};sjcl.random={randomWords:function(nwords,paranoia){var out=[],i,readiness=this.isReady(paranoia),g;if(readiness===this._NOT_READY){throw new sjcl.exception.notReady("generator isn't seeded");}else if(readiness&this._REQUIRES_RESEED){this._reseedFromPools(!(readiness&this._READY));}
270 | for(i=0;i0){estimatedEntropy++;tmp=tmp>>>1;}}}
279 | this._pools[robin].update([id,this._eventId++,2,estimatedEntropy,t,data.length].concat(data));}
280 | break;case"string":if(estimatedEntropy===undefined){estimatedEntropy=data.length;}
281 | this._pools[robin].update([id,this._eventId++,3,estimatedEntropy,t,data.length]);this._pools[robin].update(data);break;default:err=1;}
282 | if(err){throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");}
283 | this._poolEntropy[robin]+=estimatedEntropy;this._poolStrength+=estimatedEntropy;if(oldReady===this._NOT_READY){if(this.isReady()!==this._NOT_READY){this._fireEvent("seeded",Math.max(this._strength,this._poolStrength));}
284 | this._fireEvent("progress",this.getProgress());}},isReady:function(paranoia){var entropyRequired=this._PARANOIA_LEVELS[(paranoia!==undefined)?paranoia:this._defaultParanoia];if(this._strength&&this._strength>=entropyRequired){return(this._poolEntropy[0]>this._BITS_PER_RESEED&&(new Date()).valueOf()>this._nextReseed)?this._REQUIRES_RESEED|this._READY:this._READY;}else{return(this._poolStrength>=entropyRequired)?this._REQUIRES_RESEED|this._NOT_READY:this._NOT_READY;}},getProgress:function(paranoia){var entropyRequired=this._PARANOIA_LEVELS[paranoia?paranoia:this._defaultParanoia];if(this._strength>=entropyRequired){return 1.0;}else{return(this._poolStrength>entropyRequired)?1.0:this._poolStrength/entropyRequired;}},startCollectors:function(){if(this._collectorsStarted){return;}
285 | if(window.addEventListener){window.addEventListener("load",this._loadTimeCollector,false);window.addEventListener("mousemove",this._mouseCollector,false);}else if(document.attachEvent){document.attachEvent("onload",this._loadTimeCollector);document.attachEvent("onmousemove",this._mouseCollector);}
286 | else{throw new sjcl.exception.bug("can't attach event");}
287 | this._collectorsStarted=true;},stopCollectors:function(){if(!this._collectorsStarted){return;}
288 | if(window.removeEventListener){window.removeEventListener("load",this._loadTimeCollector,false);window.removeEventListener("mousemove",this._mouseCollector,false);}else if(window.detachEvent){window.detachEvent("onload",this._loadTimeCollector);window.detachEvent("onmousemove",this._mouseCollector);}
289 | this._collectorsStarted=false;},addEventListener:function(name,callback){this._callbacks[name][this._callbackI++]=callback;},removeEventListener:function(name,cb){var i,j,cbs=this._callbacks[name],jsTemp=[];for(j in cbs){if(cbs.hasOwnProperty(j)&&cbs[j]===cb){jsTemp.push(j);}}
290 | for(i=0;i=1<this._strength){this._strength=strength;}
295 | this._reseedCount++;this._reseed(reseedData);},_mouseCollector:function(ev){var x=ev.x||ev.clientX||ev.offsetX||0,y=ev.y||ev.clientY||ev.offsetY||0;sjcl.random.addEntropy([x,y],2,"mouse");},_loadTimeCollector:function(ev){sjcl.random.addEntropy((new Date()).valueOf(),2,"loadtime");},_fireEvent:function(name,arg){var j,cbs=sjcl.random._callbacks[name],cbsTemp=[];for(j in cbs){if(cbs.hasOwnProperty(j)){cbsTemp.push(cbs[j]);}}
296 | for(j=0;j