subscription_fu 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +16 -0
- data/LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +6 -0
- data/app/models/subscription_fu/plan.rb +43 -0
- data/app/models/subscription_fu/subscription.rb +117 -0
- data/app/models/subscription_fu/transaction.rb +131 -0
- data/config/locales/en.yml +7 -0
- data/examples/routes.rb +7 -0
- data/examples/subscriptions_controller.rb +12 -0
- data/examples/transactions_controller.rb +33 -0
- data/lib/generators/subscription_fu/install_generator.rb +23 -0
- data/lib/generators/subscription_fu/templates/en.yml +17 -0
- data/lib/generators/subscription_fu/templates/initializer.rb +24 -0
- data/lib/generators/subscription_fu/templates/migration.rb +38 -0
- data/lib/subscription_fu/config.rb +36 -0
- data/lib/subscription_fu/engine.rb +5 -0
- data/lib/subscription_fu/models.rb +54 -0
- data/lib/subscription_fu/paypal.rb +101 -0
- data/lib/subscription_fu/railtie.rb +12 -0
- data/lib/subscription_fu/version.rb +3 -0
- data/lib/subscription_fu.rb +15 -0
- data/spec/app/.gitignore +1 -0
- data/spec/app/Rakefile +8 -0
- data/spec/app/app/controllers/application_controller.rb +6 -0
- data/spec/app/app/models/initiator.rb +2 -0
- data/spec/app/app/models/subject.rb +4 -0
- data/spec/app/app/views/layouts/application.html.haml +6 -0
- data/spec/app/config/application.rb +11 -0
- data/spec/app/config/boot.rb +4 -0
- data/spec/app/config/database.yml +18 -0
- data/spec/app/config/environment.rb +5 -0
- data/spec/app/config/environments/development.rb +18 -0
- data/spec/app/config/environments/test.rb +22 -0
- data/spec/app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/app/config/initializers/inflections.rb +2 -0
- data/spec/app/config/initializers/secret_token.rb +2 -0
- data/spec/app/config/initializers/subscription_fu.rb +28 -0
- data/spec/app/config/locales/subscription_fu.en.yml +17 -0
- data/spec/app/config/routes.rb +3 -0
- data/spec/app/config.ru +4 -0
- data/spec/app/db/.gitignore +1 -0
- data/spec/app/db/migrate/20110516061428_create_subjects.rb +13 -0
- data/spec/app/db/migrate/20110516061443_create_initiators.rb +14 -0
- data/spec/app/db/migrate/20110516070948_create_subscription_fu_tables.rb +38 -0
- data/spec/app/db/schema.rb +62 -0
- data/spec/app/script/rails +10 -0
- data/spec/factories/initiator.rb +4 -0
- data/spec/factories/subject.rb +2 -0
- data/spec/factories/subscription.rb +6 -0
- data/spec/factories/transaction.rb +7 -0
- data/spec/models/subscription_spec.rb +277 -0
- data/spec/models/subscription_transaction_spec.rb +135 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/paypal_test_helper.rb +82 -0
- data/subscription_fu.gemspec +23 -0
- metadata +134 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module SubscriptionFu
|
2
|
+
module Models
|
3
|
+
def self.included(base)
|
4
|
+
base.send(:extend, ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def needs_subscription
|
9
|
+
send(:include, InstanceMethods)
|
10
|
+
has_many :subscriptions, :class_name => "SubscriptionFu::Subscription", :as => :subject, :dependent => :destroy
|
11
|
+
delegate :plan, :sponsored?, :canceled?, :prefix => :subscription, :to => :active_subscription, :allow_nil => true
|
12
|
+
delegate :plan, :prefix => :upcoming_subscription, :to => :upcoming_subscription, :allow_nil => true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def human_description_for_subscription
|
18
|
+
self.class.model_name.human
|
19
|
+
end
|
20
|
+
|
21
|
+
def active_subscription
|
22
|
+
@active_subscription ||= subscriptions.current(Time.now).last
|
23
|
+
end
|
24
|
+
|
25
|
+
def active_subscription?
|
26
|
+
!active_subscription.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def upcoming_subscription
|
30
|
+
active_subscription ? active_subscription.next_subscriptions.activated.last : nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def pending_transaction(identifier)
|
34
|
+
sub = subscriptions.last
|
35
|
+
if sub.activated?
|
36
|
+
logger.info("Latest subscription is already activated")
|
37
|
+
nil
|
38
|
+
else
|
39
|
+
sub.transactions.initiated.find_by_identifier(identifier)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_next_subscription(plan_key)
|
44
|
+
if active_subscription
|
45
|
+
# TODO refactor
|
46
|
+
subscriptions.build_for_initializing(plan_key, active_subscription.successor_start_date(plan_key), active_subscription.successor_billing_start_date, active_subscription)
|
47
|
+
else
|
48
|
+
subscriptions.build_for_initializing(plan_key)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "rest-client"
|
2
|
+
|
3
|
+
class SubscriptionFu::Paypal
|
4
|
+
|
5
|
+
def self.paypal
|
6
|
+
new(SubscriptionFu.config.paypal_api_user_id,
|
7
|
+
SubscriptionFu.config.paypal_api_pwd,
|
8
|
+
SubscriptionFu.config.paypal_api_sig,
|
9
|
+
SubscriptionFu.config.paypal_nvp_api_url,
|
10
|
+
Rails.logger)
|
11
|
+
end
|
12
|
+
|
13
|
+
class Failure < RuntimeError
|
14
|
+
attr_reader :request_opts, :response
|
15
|
+
|
16
|
+
def initialize(request_opts, response)
|
17
|
+
@request_opts = request_opts
|
18
|
+
@response = response
|
19
|
+
super "PayPal did not return success: #{@response.inspect}"
|
20
|
+
end
|
21
|
+
alias :data :request_opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(user, pwd, sig, api_url, logger)
|
25
|
+
@user = user
|
26
|
+
@pwd = pwd
|
27
|
+
@sig = sig
|
28
|
+
@api_url = api_url
|
29
|
+
@logger = logger
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_checkout(return_url, cancel_url, email, maxamt, currency_code, desc)
|
33
|
+
res = call_paypal('SetExpressCheckout',
|
34
|
+
'RETURNURL' => return_url,
|
35
|
+
'CANCELURL' => cancel_url,
|
36
|
+
'EMAIL' => email,
|
37
|
+
'NOSHIPPING' => 1,
|
38
|
+
'MAXAMT' => maxamt,
|
39
|
+
'PAYMENTREQUEST_0_AMT' => 0,
|
40
|
+
'PAYMENTREQUEST_0_CURRENCYCODE' => currency_code,
|
41
|
+
'L_BILLINGTYPE0' => 'RecurringPayments',
|
42
|
+
'L_BILLINGAGREEMENTDESCRIPTION0' => desc)
|
43
|
+
res['TOKEN'].first
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_recurring(token, start_date, amt, taxamt, currency_code, desc)
|
47
|
+
res = call_paypal('CreateRecurringPaymentsProfile',
|
48
|
+
'PROFILESTARTDATE' => start_date.utc.iso8601,
|
49
|
+
'BILLINGPERIOD' => 'Month',
|
50
|
+
'BILLINGFREQUENCY' => 1,
|
51
|
+
'MAXFAILEDPAYMENTS' => 0,
|
52
|
+
'AUTOBILLOUTAMT' => 'AddToNextBilling',
|
53
|
+
'AMT' => amt,
|
54
|
+
'TAXAMT' => taxamt,
|
55
|
+
'CURRENCYCODE' => currency_code,
|
56
|
+
'DESC' => desc,
|
57
|
+
'TOKEN' => token)
|
58
|
+
[res['PROFILEID'].first, res['PROFILESTATUS'].first || res['STATUS'].first]
|
59
|
+
end
|
60
|
+
|
61
|
+
def recurring_details(profile_id)
|
62
|
+
utc = ActiveSupport::TimeZone.new("UTC")
|
63
|
+
res = call_paypal('GetRecurringPaymentsProfileDetails', 'PROFILEID' => profile_id)
|
64
|
+
{ :id => res['PROFILEID'].first,
|
65
|
+
:status => res['PROFILESTATUS'].first || res['STATUS'].first,
|
66
|
+
:next_billing_date => utc.parse(res['NEXTBILLINGDATE'].first.to_s),
|
67
|
+
:start_date => utc.parse(res['PROFILESTARTDATE'].first.to_s),
|
68
|
+
:cycles_completed => res['NUMCYCLESCOMPLETED'].first.to_i,
|
69
|
+
:outstanding_balance => res['OUTSTANDINGBALANCE'].first.to_f,
|
70
|
+
:last_payment_date => utc.parse(res['LASTPAYMENTDATE'].first.to_s) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def cancel_recurring(profile_id, reason)
|
74
|
+
call_paypal('ManageRecurringPaymentsProfileStatus',
|
75
|
+
'PROFILEID' => profile_id,
|
76
|
+
'ACTION' => 'Cancel',
|
77
|
+
'NOTE' => I18n.t(reason, :scope => [:subscription_fu, :subscription, :cancel_notes]))
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def call_paypal(method, opts)
|
83
|
+
full_opts = paypal_api_opts(method, opts)
|
84
|
+
CGI.parse(RestClient.post(@api_url, full_opts)).tap do |result|
|
85
|
+
if result['ACK'].first != 'Success'
|
86
|
+
@logger.warn("PayPal did not return success for call to #{method}")
|
87
|
+
@logger.info(" called with: #{full_opts.inspect}")
|
88
|
+
@logger.info(" result: #{result.inspect}")
|
89
|
+
raise Failure.new(full_opts, result)
|
90
|
+
else
|
91
|
+
@logger.info("PayPal returned success for call to #{method}")
|
92
|
+
@logger.info(" result: #{result.inspect}")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def paypal_api_opts(method, opts)
|
98
|
+
opts.merge('USER' => @user, 'PWD' => @pwd, 'SIGNATURE' => @sig, 'VERSION' => '65.0', 'METHOD' => method)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module SubscriptionFu
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
config.subscription_fu = SubscriptionFu.config
|
4
|
+
initializer "subscription_fu.extend.active_record" do |app|
|
5
|
+
ActiveSupport.on_load :active_record do
|
6
|
+
include SubscriptionFu::Models
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require "subscription_fu/models"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SubscriptionFu
|
2
|
+
autoload :Config, "subscription_fu/config"
|
3
|
+
autoload :Paypal, "subscription_fu/paypal"
|
4
|
+
|
5
|
+
def self.config
|
6
|
+
@config ||= SubscriptionFu::Config.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.friendly_token
|
10
|
+
ActiveSupport::SecureRandom.base64(44).tr('+/=', 'xyz')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'subscription_fu/railtie'
|
15
|
+
require 'subscription_fu/engine'
|
data/spec/app/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
log
|
data/spec/app/Rakefile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
|
+
|
4
|
+
require File.expand_path('../config/application', __FILE__)
|
5
|
+
|
6
|
+
require 'rake'
|
7
|
+
|
8
|
+
Rails.application.load_tasks
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# SQLite version 3.x
|
2
|
+
# gem install sqlite3-ruby (not necessary on OS X Leopard)
|
3
|
+
development:
|
4
|
+
adapter: sqlite3
|
5
|
+
database: db/development.sqlite3
|
6
|
+
pool: 5
|
7
|
+
timeout: 5000
|
8
|
+
|
9
|
+
# Warning: The database defined as "test" will be erased and
|
10
|
+
# re-generated from your development database when you run "rake".
|
11
|
+
# Do not set this db to the same as development or production.
|
12
|
+
test:
|
13
|
+
adapter: sqlite3
|
14
|
+
database: ":memory:"
|
15
|
+
|
16
|
+
production:
|
17
|
+
adapter: sqlite3
|
18
|
+
database: ":memory:"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
RailsApp::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/environment.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 webserver when you make code changes.
|
7
|
+
config.cache_classes = false
|
8
|
+
|
9
|
+
# Log error messages when you accidentally call methods on nil.
|
10
|
+
config.whiny_nils = true
|
11
|
+
|
12
|
+
# Show full error reports and disable caching
|
13
|
+
config.consider_all_requests_local = true
|
14
|
+
config.action_view.debug_rjs = true
|
15
|
+
config.action_controller.perform_caching = false
|
16
|
+
|
17
|
+
config.active_support.deprecation = :stderr
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RailsApp::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/environment.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
|
+
# Log error messages when you accidentally call methods on nil.
|
11
|
+
config.whiny_nils = true
|
12
|
+
|
13
|
+
# Show full error reports and disable caching
|
14
|
+
config.consider_all_requests_local = true
|
15
|
+
config.action_controller.perform_caching = false
|
16
|
+
|
17
|
+
# Disable request forgery protection in test environment
|
18
|
+
config.action_controller.allow_forgery_protection = false
|
19
|
+
|
20
|
+
config.action_dispatch.show_exceptions = false
|
21
|
+
config.active_support.deprecation = :stderr
|
22
|
+
end
|
@@ -0,0 +1,7 @@
|
|
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!
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class MyCustomPlan < SubscriptionFu::Plan
|
2
|
+
attr_accessor :tickets
|
3
|
+
|
4
|
+
def private_events?
|
5
|
+
price > 1000
|
6
|
+
end
|
7
|
+
|
8
|
+
def custom_email_from?
|
9
|
+
price > 1000
|
10
|
+
end
|
11
|
+
|
12
|
+
def extra_promotion?
|
13
|
+
! free_plan?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
RailsApp::Application.configure do
|
18
|
+
config.subscription_fu.paypal_api_user_id = "michae_1272617165_biz_api1.mobalean.com"
|
19
|
+
config.subscription_fu.paypal_api_pwd = "1272617171"
|
20
|
+
config.subscription_fu.paypal_api_sig = "ATpPRKe6SEGaLcgDfFD-kBQgVsGuA9iFQwK6d4x6Qs4iti0XYRkZQl9Q"
|
21
|
+
|
22
|
+
config.subscription_fu.plan_class_name = "MyCustomPlan"
|
23
|
+
|
24
|
+
config.subscription_fu.add_free_plan 'free', :tickets => 100
|
25
|
+
config.subscription_fu.add_plan 'basic', 1000, :tickets => 100
|
26
|
+
config.subscription_fu.add_plan 'premium', 5000, :tickets => 200
|
27
|
+
config.subscription_fu.add_plan 'profess', 20000, :tickets => 1000
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
en:
|
2
|
+
subscription_fu:
|
3
|
+
subscription:
|
4
|
+
# description used for Paypal
|
5
|
+
description: "%{plan_name} subscription for %{subject_desc}, %{price} per month"
|
6
|
+
# cancel notes used as description for Paypal
|
7
|
+
cancel_notes:
|
8
|
+
update: "Changed subscription plan"
|
9
|
+
cancel: "Subscription cancelled"
|
10
|
+
plan:
|
11
|
+
options:
|
12
|
+
# match this section with the plan names you are using in
|
13
|
+
# config/initializers/subscription_fu.rb to get human readable names
|
14
|
+
profess: Professional
|
15
|
+
premium: Premium
|
16
|
+
basic: Basic
|
17
|
+
free: Free
|
data/spec/app/config.ru
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.sqlite3
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class CreateSubscriptionFuTables < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table "subscriptions", :force => true do |t|
|
4
|
+
t.references "subject", :polymorphic => true
|
5
|
+
t.references "prev_subscription"
|
6
|
+
t.string "plan_key", :limit => 10, :null => false
|
7
|
+
t.boolean "sponsored", :null => false, :default => false
|
8
|
+
t.string "paypal_profile_id"
|
9
|
+
t.datetime "starts_at", :null => false
|
10
|
+
t.datetime "billing_starts_at", :null => false
|
11
|
+
t.datetime "activated_at"
|
12
|
+
t.datetime "canceled_at"
|
13
|
+
t.string "cancel_reason", :limit => 10
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
|
17
|
+
add_index "subscriptions", ["subject_id", "subject_type"]
|
18
|
+
|
19
|
+
create_table "subscription_transactions" do |t|
|
20
|
+
t.references "subscription", :null => false
|
21
|
+
t.references "initiator", :null => false, :polymorphic => true
|
22
|
+
t.string "action", :limit => 15, :null => false
|
23
|
+
t.string "status", :limit => 15, :null => false
|
24
|
+
t.string "gateway", :limit => 10, :null => false
|
25
|
+
t.string "identifier"
|
26
|
+
t.references "related_transaction"
|
27
|
+
t.timestamps
|
28
|
+
end
|
29
|
+
|
30
|
+
add_index "subscription_transactions", ["identifier"]
|
31
|
+
add_index "subscription_transactions", ["subscription_id"]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.down
|
35
|
+
drop_table "subscriptions"
|
36
|
+
drop_table "subscription_transactions"
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your
|
6
|
+
# database schema. If you need to create the application database on another
|
7
|
+
# system, you should be using db:schema:load, not running all the migrations
|
8
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
9
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
10
|
+
#
|
11
|
+
# It's strongly recommended to check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(:version => 20110516070948) do
|
14
|
+
|
15
|
+
create_table "initiators", :force => true do |t|
|
16
|
+
t.string "desc"
|
17
|
+
t.string "email"
|
18
|
+
t.datetime "created_at"
|
19
|
+
t.datetime "updated_at"
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table "subjects", :force => true do |t|
|
23
|
+
t.string "desc"
|
24
|
+
t.datetime "created_at"
|
25
|
+
t.datetime "updated_at"
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table "subscription_transactions", :force => true do |t|
|
29
|
+
t.integer "subscription_id", :null => false
|
30
|
+
t.integer "initiator_id", :null => false
|
31
|
+
t.string "initiator_type", :null => false
|
32
|
+
t.string "action", :limit => 15, :null => false
|
33
|
+
t.string "status", :limit => 15, :null => false
|
34
|
+
t.string "gateway", :limit => 10, :null => false
|
35
|
+
t.string "identifier"
|
36
|
+
t.integer "related_transaction_id"
|
37
|
+
t.datetime "created_at"
|
38
|
+
t.datetime "updated_at"
|
39
|
+
end
|
40
|
+
|
41
|
+
add_index "subscription_transactions", ["identifier"], :name => "index_subscription_transactions_on_identifier"
|
42
|
+
add_index "subscription_transactions", ["subscription_id"], :name => "index_subscription_transactions_on_subscription_id"
|
43
|
+
|
44
|
+
create_table "subscriptions", :force => true do |t|
|
45
|
+
t.integer "subject_id"
|
46
|
+
t.string "subject_type"
|
47
|
+
t.integer "prev_subscription_id"
|
48
|
+
t.string "plan_key", :limit => 10, :null => false
|
49
|
+
t.boolean "sponsored", :default => false, :null => false
|
50
|
+
t.string "paypal_profile_id"
|
51
|
+
t.datetime "starts_at", :null => false
|
52
|
+
t.datetime "billing_starts_at", :null => false
|
53
|
+
t.datetime "activated_at"
|
54
|
+
t.datetime "canceled_at"
|
55
|
+
t.string "cancel_reason", :limit => 10
|
56
|
+
t.datetime "created_at"
|
57
|
+
t.datetime "updated_at"
|
58
|
+
end
|
59
|
+
|
60
|
+
add_index "subscriptions", ["subject_id", "subject_type"], :name => "index_subscriptions_on_subject_id_and_subject_type"
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,10 @@
|
|
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
|
+
ENV_PATH = File.expand_path('../../config/environment', __FILE__)
|
5
|
+
BOOT_PATH = File.expand_path('../../config/boot', __FILE__)
|
6
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
7
|
+
ROOT_PATH = File.expand_path('../..', __FILE__)
|
8
|
+
|
9
|
+
require BOOT_PATH
|
10
|
+
require 'rails/commands'
|