Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of github.com:ShinobiDevs/bascout

  • Loading branch information...
commit b589a699b242c75f846311811bab6d8b9c0f4aa0 2 parents e68aed1 + 4cb74a3
@eladmeidar eladmeidar authored
View
7 Gemfile
@@ -34,6 +34,8 @@ gem "thin"
gem 'metamagic'
gem "transfermarkt"
+gem 'simpler_state_machine'
+
group :doc do
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', require: false
@@ -46,6 +48,11 @@ group :development do
gem "better_errors"
gem "binding_of_caller"
gem 'web-console', '~> 1.0.1'
+
+ gem "pry"
+ gem 'pry-remote'
+ gem 'pry-stack_explorer'
+ gem 'pry-debugger'
end
group :production do
View
28 Gemfile.lock
@@ -80,10 +80,17 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.6.3)
+ columnize (0.3.6)
connection_pool (1.2.0)
country_select (1.2.0)
daemons (1.1.9)
debug_inspector (0.0.2)
+ debugger (1.6.1)
+ columnize (>= 0.3.1)
+ debugger-linecache (~> 1.2.0)
+ debugger-ruby_core_source (~> 1.2.3)
+ debugger-linecache (1.2.0)
+ debugger-ruby_core_source (1.2.3)
devise (3.2.2)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
@@ -155,6 +162,7 @@ GEM
treetop (~> 1.4.8)
metamagic (2.0.6)
rails (>= 3.0.0)
+ method_source (0.8.2)
mime-types (1.25.1)
mini_portile (0.5.2)
minitest (4.7.5)
@@ -187,6 +195,19 @@ GEM
origin (2.0.0)
orm_adapter (0.5.0)
polyglot (0.3.3)
+ pry (0.9.12.4)
+ coderay (~> 1.0)
+ method_source (~> 0.8)
+ slop (~> 3.4)
+ pry-debugger (0.2.2)
+ debugger (~> 1.3)
+ pry (~> 0.9.10)
+ pry-remote (0.1.7)
+ pry (~> 0.9)
+ slop (~> 3.0)
+ pry-stack_explorer (0.4.9.1)
+ binding_of_caller (>= 0.7)
+ pry (>= 0.9.11)
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
rack (1.5.2)
@@ -270,10 +291,12 @@ GEM
actionpack (>= 4.0.0, < 4.1)
activemodel (>= 4.0.0, < 4.1)
simple_oauth (0.2.0)
+ simpler_state_machine (0.0.5)
sinatra (1.4.4)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
+ slop (3.4.7)
soccerwiki (0.0.4)
httparty
nokogiri
@@ -352,6 +375,10 @@ DEPENDENCIES
mongoid!
moped!
omniauth-facebook
+ pry
+ pry-debugger
+ pry-remote
+ pry-stack_explorer
quiet_assets
rails (= 4.0.0)
rails_admin
@@ -361,6 +388,7 @@ DEPENDENCIES
sdoc
sidekiq
simple_form
+ simpler_state_machine
sinatra
soccerwiki
thin
View
15 config/application.rb
@@ -23,19 +23,10 @@
module Bascout
class Application < Rails::Application
- # Settings in config/environments/* take precedence over those specified here.
- # Application configuration should go into files in config/initializers
- # -- all .rb files in that directory are automatically loaded.
+ config.autoload_paths += Dir["#{config.root}/lib/"]
+ config.autoload_paths += Dir["#{config.root}/lib/banking/"]
+ config.autoload_paths += Dir["#{config.root}/app/workers"]
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
- # config.time_zone = 'Central Time (US & Canada)'
-
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
- # config.i18n.default_locale = :de
-
- config.autoload_paths += ["#{config.root}/app/workers"]
config.generators do |g|
g.orm = :mongoid
g.template_engine = :haml
View
35 lib/banking/buyable.rb
@@ -0,0 +1,35 @@
+module Banking::Buyable
+ module Exceptions
+ class NotImplemented < StandardError; end
+ end
+
+ def self.included(base)
+ base.class_eval do
+ extend ClassMethods
+ include InstanceMethods
+ end
+ end
+
+ module ClassMethods
+ def available_payment_methods
+ []
+ end
+ end
+
+ module InstanceMethods
+ def buyable?
+ true
+ end
+
+ def payment_tokens
+ Banking::PaymentToken.where(:buyable_id => self.id.to_s, :buyable_type => self.class.name)
+ end
+
+ def payment_received(payment_token)
+ raise Exceptions::NotImplemented, "#payment_received is not implemented for #{self.class.name}"
+ end
+
+ def payment_failed
+ end
+ end
+end
View
44 lib/banking/buyerr.rb
@@ -0,0 +1,44 @@
+module Banking::Buyerr
+ module Exceptions
+ class ItemNotOrderable < StandardError; end
+ end
+
+ def self.included(base)
+ base.class_eval do
+ extend ClassMethods
+ include InstanceMethods
+ end
+ end
+
+ module ClassMethods
+ end
+
+ module InstanceMethods
+ def buy!(product, buy_options = {})
+
+ # Raise an exception if the product is not a buyable class
+ if product.blank? || !(product.buyable?)
+ raise(Exceptions::ItemNotOrderable, "Attempt to buy a non buyable item. #{product.class.name}")
+ end
+
+ token = nil
+ Banking::PaymentToken.transaction do
+ token = Banking::PaymentToken.create!(:buyable => product,
+ :buyable_id => product.id.to_s,
+ :buyable_type => product.class.name,
+ :buyer_id => self.id,
+ :seller_id => product.seller_id,
+ :seller_type => product.seller.class.name,
+ :amount => product.item_price(buy_options),
+ :buyer_banking_account_id => self.banking_account.id,
+ :seller_banking_account_id => product.seller.banking_account.id,
+ :buy_options => buy_options,
+ :currency_id => 1,
+ :payment_source => "website")
+ end
+
+ # Return the response generated by the payment method elected for payment.
+ return token.response
+ end
+ end
+end
View
65 lib/banking/payment_method.rb
@@ -0,0 +1,65 @@
+class Banking::PaymentMethod
+
+ # Modules
+ module Exceptions
+ class NotImplemented < StandardError; end
+ end
+
+ attr_reader :payment_token
+
+ def initialize(payment_token)
+ if payment_token.blank?
+ raise (Exceptions::ParamsMissing, "buyer and payment token must be specified")
+ end
+
+ @payment_token = payment_token
+ end
+
+ # Stem implementation for the #can_pay? boolean method, that should implement the requirements
+ # for paying with a specific payment method
+ def can_pay?
+ raise Exceptions::NotImplemented, "#can_pay? is not implemented for #{self.class.name}"
+ end
+
+ # Initialize a payment transaction, fires #pay! and #on_success if successful. should it fails it
+ # notates the payment token with the failure and fires the on_failure callback
+ def attempt_payment!
+ begin
+ pay!
+ on_success
+ rescue Exception => e
+ on_failure(e)
+ end
+ end
+
+ def pay!
+ raise Exceptions::NotImplemented, "#pay is not implemented for #{self.class.name}"
+ end
+
+ def on_success
+ raise Exceptions::NotImplemented, "#on_success is not implemented for #{self.class.name}"
+ end
+
+ def on_failure(e)
+ payment_token.fail
+
+ response = payment_token.response
+ response.success = false
+ response.message = "An unexpected error occurred during payment"
+ response.body = {:error => e.message}
+
+ raise e if Rails.env.test?
+ end
+
+ def attempt_refund!
+ begin
+ refund!
+ rescue Exception => e
+ on_failure(e)
+ end
+ end
+
+ def refund!
+ raise Exceptions::NotImplemented, "#refund! is not implemented for #{self.class.name}"
+ end
+end
View
114 lib/banking/payment_token.rb
@@ -0,0 +1,114 @@
+class Banking::PaymentToken
+ include Mongoid::Document
+ include Mongoid::Timestamps
+ include Dollarr
+ include Banking::Token
+ include SimplerStateMachine
+
+ class TokenResponse
+
+ RESULTS = Enum.new(:pending, :completed, :requires_action)
+
+ attr_writer :success
+ attr_writer :result
+ attr_accessor :message
+ attr_accessor :body
+ attr_accessor :redirect_url
+
+ def initialize
+ self.success = true
+ end
+
+ def success?
+ @success
+ end
+ end
+
+ field :token, type: String
+ field :amount_in_cents, type: Integer
+ field :buy_options, type: Hash
+ field :buyable_id, type: String
+ field :buyable_type, type: String
+ field :buyer_id, type: Integer
+ field :seller_id, type: Integer
+ field :buyer_banking_account_id, type: Integer
+ field :seller_banking_account_id, type: Integer
+ field :currency_id, type: Integer
+ field :payment_source, type: String
+ field :payment_method, type: String
+ field :created_at, type: DateTime
+ field :updated_at, type: DateTime
+ field :status, type: Integer
+ field :seller_type, type: String
+ field :payment_data, type: Hash
+
+ # Associations
+ belongs_to :seller, :polymorphic => true
+ belongs_to :buyer, :class_name => "::User", :foreign_key => :buyer_id
+
+ belongs_to :buyer_banking_account, :class_name => "Account", :foreign_key => "buyer_banking_account_id"
+ belongs_to :seller_banking_account, :class_name => "Account", :foreign_key => "seller_banking_account_id"
+
+ #has_many :transactions, :as => :banking_token, :class_name => "Banking::Transaction"
+
+ attr_accessible :amount, :amount_in_cents, :buy_options, :buyable, :buyable_id, :buyable_type, :buyer_id, :buyer_banking_account_id, :seller_id, :seller_type, :seller_banking_account_id, :currency_id, :payment_source, :token, :status, :payment_method
+ serialize :buy_options, Hash
+ serialize :payment_data, Hash
+
+ state_machine do |sm|
+ sm.state_field "status"
+
+ sm.initial_state "pending"
+ sm.add_state "success", :after_enter => :payment_successful, :on_error => :payment_failed
+ sm.add_state "failed", :after_enter => :payment_failed
+ sm.add_state "refunded", :after_enter => :payment_refunded
+ sm.add_transition :succeed, :from => "pending", :to => "success"
+ sm.add_transition :fail, :from => ["pending", "success"], :to => "failed"
+ sm.add_transition :refund, :from => ["success"], :to => "refunded"
+ end
+
+ def response
+ if @response.nil?
+ @response = TokenResponse.new
+ @response.result = TokenResponse::RESULTS[:pending].index
+ else
+ @response
+ end
+ end
+
+ def buyable
+ @buyable ||= buyable_type.constantize.find(buyable_id)
+ end
+
+ def buyable=(value)
+ @buyable = value
+ end
+
+ def process
+ self.buyable.available_payment_methods.each do |payment_method_itr|
+ if payment_method_itr.can_pay?
+ self.payment_method = payment_method_itr
+ method = self.payment_method.constantize.new(self)
+ method.attempt_payment!
+ self.save!
+ break
+ end
+ end
+ end
+
+ def payment_successful
+ buyable.payment_received(self)
+ end
+
+ def payment_failed
+ buyable.payment_failed
+ end
+
+ def payment_refunded
+ # start processing payment refund from payment gateway
+ method = self.payment_method.constantize.new(self)
+ method.attempt_refund!
+
+ buyable.payment_refunded
+ end
+end
View
49 lib/banking/token.rb
@@ -0,0 +1,49 @@
+module Banking::Token
+
+ STATUSES = Enum.new(:pending, :success, :failure)
+
+ def self.included(base)
+ base.class_eval do
+ # Callbacks
+ before_create :generate_token
+ after_create :process
+
+ # Associations
+ #has_many :transactions
+ #has_many :account_activities
+
+ extend ClassMethods
+ include InstanceMethods
+ end
+ end
+
+ module Exceptions
+ class NotImplemented < StandardError; end
+ end
+
+ module ClassMethods
+ end
+
+ module InstanceMethods
+ def pending?
+ status == STATUSES[:pending].index
+ end
+
+ def successful?
+ status == STATUSES[:success].index
+ end
+
+ def failed?
+ status == STATUSES[:failed].index
+ end
+
+ protected
+ def process
+ raise Exceptions::NotImplemented, "#process_token is not implemented for #{self.class.name}"
+ end
+
+ def generate_token
+ self.token = SecureRandom.hex(10)
+ end
+ end
+end
View
55 lib/dolarr.rb
@@ -0,0 +1,55 @@
+=begin
+
+Dollarr
+------
+UPDATED: 18/12/2011 - Elad
+a simple class that enables representation of amounts in dollars programatically but actually stores the cents amount equivalent.
+
+USAGE:
+-----
+Add to your db backed class:
+
+ include Dollar
+
+once you do that, all the columns in your model's table that end with the "in_cents" prefix will be wrapped with helpers carrying the same
+name just without the "in_cents" prefix, returning the dollar amount in cents.
+
+for example:
+ class Whore
+ include Dollar
+
+ # field_name | type
+ # cost_in_cents | int
+ end
+
+> a = Whore.new
+#<Whore:0x105886b10 cost_in_cents: 0 >
+> a.cost = 7
+7
+> a.cost_in_cents
+700
+
+=end
+
+module Dollarr
+ def self.included(base)
+ base.class_eval do
+ if self.ancestors.include?(ActiveRecord::Base)
+ attr_names = column_names
+ elsif self.included_modules.include?(Mongoid::Document)
+ attr_names = fields.keys
+ end
+ attr_names.grep(/in_cents/).each do |cents_column|
+ dollar_method = cents_column.gsub(/_in_cents/, '')
+
+ define_method(dollar_method) do
+ send("#{cents_column}").to_f / 100.0
+ end
+
+ define_method(dollar_method + "=") do |amount_in_dollar|
+ send("#{cents_column}=", amount_in_dollar.to_f * 100)
+ end
+ end
+ end
+ end
+end
View
104 lib/enum.rb
@@ -0,0 +1,104 @@
+# Enum
+# ----
+#
+# Created: Elad meidar 3/7/2012
+# Improved: MB 11/7/2012
+#
+# Usage:
+# Color = Enum.new(:Red, :Green, :Blue)
+# Color.is_a?(Enum) # => true
+# Color::Red.inspect # => "Color::Red"
+# Color::Green.is_a?(Color) # => true
+# Color::Green.is_a?(Enum::Member) # => true
+# Color::Green.index # => 1
+# Color::Blue.enum # => Color
+# values = [[255, 0, 0], [0, 255, 0], [0, 0, 255]]
+# values[Color::Green] # => [0, 255, 0]
+# Color[0] # => Color::Red
+# Color.size # => 3
+#
+# Enums are enumerable. Enum::Members are comparable.
+
+class Enum < Module
+ class Member < Module
+ attr_reader :enum, :index, :syme
+
+ def initialize(enum, index, syme)
+ @enum, @index, @syme = enum, index, syme
+ # Allow Color::Red.is_a?(Color)
+ extend enum
+ end
+
+ # Allow use of enum members as array indices
+ alias :to_int :index
+ alias :to_i :index
+
+ alias :to_sym :syme
+ alias :to_s :name
+
+ def name
+ self.syme.to_s
+ end
+
+ # Allow comparison by index
+ def <=>(other)
+ @index <=> other.index if other.respond_to?(:index)
+ end
+
+ include Comparable
+ end
+
+ def initialize(*symbols, &block)
+ @members = []
+ symbols.each do |symbol|
+ add_member(symbol)
+ end
+ super(&block)
+ end
+
+ def add_member(symbol)
+ index = @members.count
+ symbol = symbol.to_s.sub(/^[a-z]/){|letter| letter.upcase}.to_sym
+ member = Enum::Member.new(self, index, symbol)
+ const_set(symbol, member)
+ @members << member
+ return member
+ end
+
+ def all
+ all = {}
+ @members.each_with_index do |member, index|
+ all[index] = member
+ end
+ end
+
+ def [](val)
+ if val.is_a?(Numeric)
+ @members[val]
+ elsif val.is_a?(Symbol)
+ @members.select {|member| member.syme == val }.try(:first)
+ elsif val.is_a?(String)
+ @members.select {|member| member.name == val }.try(:first)
+ end
+ end
+
+ def size
+ @members.size
+ end
+
+ alias :length :size
+
+ def first(*args)
+ @members.first(*args)
+ end
+
+ def last(*args)
+ @members.last(*args)
+ end
+
+ def each(&block)
+ @members.each(&block)
+ end
+
+ include Enumerable
+end
Please sign in to comment.
Something went wrong with that request. Please try again.