Coupons is a Rails engine for creating discount coupons.
Add this line to your application's Gemfile:
gem 'coupons'
You also need one pagination library. You can choose between paginate or kaminari, so make sure one of these libs is added to your Gemfile as well.
gem 'paginate'
# or
gem 'kaminari'
And then execute:
$ bundle
Or install it yourself as:
$ gem install coupons
After installing Coupons
, execute rake db:migrate
and add the following line to your config/routes.rb
file.
mount Coupons::Engine => '/', as: 'coupons_engine'
You can visit /coupons
to access the dashboard.
There are two types of coupons: percentage or amount.
- percentage: applies the percentage discount to total amount.
- amount: applies the amount discount to the total amount.
The coupon code is generated with Coupons.configuration.generator
. By default, it creates a 6-chars long uppercased alpha-numeric code. You can use any object that implements the call
method and returns a string. The following implementation generates coupon codes like AWESOME-B7CB
.
Coupons.configure do |config|
config.generator = proc do
token = SecureRandom.hex[0, 4].upcase
"AWESOME-#{token}"
end
end
You can always override the generated coupon code through the dashboard or Ruby.
Imagine that you created the coupon RAILSCONF15
as a $100 discount; you can apply it to any amount using the Coupons.apply
method. Notice that this method won't redeem the coupon code and it's supposed to be used on the checkout page.
Coupons.apply('RAILSCONF15', amount: 600.00)
#=> {:amount => 600.0, :discount => 100.0, :total => 500.0}
When a coupon is invalid/non-redeemable, it returns the discount amount as 0
.
Coupons.apply('invalid', amount: 100.00)
#=> {:amount => 100.0, :discount => 0, :total => 100.0}
To redeem the coupon you can use Coupon.redeem
.
Coupons.redeem('RAILSCONF15', amount: 600.00)
#=> {:amount => 600.0, :discount => 100.0, :total => 500.0}
coupon = Coupons::Models::Coupon.last
coupon.redemptions_count
#=> 1
coupon.redemptions
#=> [#<Coupons::Models::CouponRedemption:0x0000010e388290>]
By default, the first redeemable coupon is used. You can set any of the following strategies.
Coupons::Finders::FirstAvailable
: returns the first redeemable coupon available.Coupons::Finders::SmallerDiscount
: returns the smaller redeemable discount available.Coupons::Finders::LargerDiscount
: returns the larger redeemable discount available.
To define a different strategy, set the Coupons.configurable.finder
attribute.
Coupons.configure do |config|
config.finder = Coupons::Finders::SmallerDiscount
end
A finder can be any object that receives the coupon code and the options (which must include the amount
key). Here's how the smaller discount finder is implemented.
module Coupons
module Finders
SmallerDiscount = proc do |code, options = {}|
coupons = Models::Coupon.where(code: code).all.select(&:redeemable?)
coupons.min do |a, b|
a = a.apply(options)
b = b.apply(options)
a[:discount] <=> b[:discount]
end
end
end
end
The whole coupon interaction can be made through some helpers methods. You can extend any object with Coupons::Helpers
module. So do it in your initializer file or in your controller, whatever suits you best.
coupons = Object.new.extend(Coupons::Helpers)
Now you can do all the interactions through the coupons
variable.
Coupons has a flexible authorization system, meaning you can do whatever you want. All you have to do is defining the authorization strategy by setting Coupons.configuration.authorizer
. By default, it disables access to the production
environment, as you can see below.
Coupons.configure do |config|
config.authorizer = proc do |controller|
if Rails.env.production?
controller.render(
text: 'Coupons: not enabled in production environments',
status: 403
)
end
end
end
To define your own strategy, like doing basic authentication, you can do something like this:
Coupons.configure do |config|
config.authorizer = proc do |controller|
controller.authenticate_or_request_with_http_basic do |user, password|
user == 'admin' && password == 'sekret'
end
end
end
To be written.
To be written.
You may want to apply discounts using AJAX, so you can give instant feedback. In this case, you'll find the /coupons/apply
endpoint useful.
var response = $.get('/coupons/apply', {amount: 600.0, coupon: 'RAILSCONF15'});
response.done(function(options)) {
console.log(options);
//=> {amount: 600.0, discount: 100.0, total: 500.0}
});
If you provide invalid amount/coupon, then it'll return zero values, like {amount: 0, discount: 0, total: 0}
.
Coupons uses I18n. It has support for en
and pt-BR
. You can contribute with your language by translating the file config/en.yml.
- Before implementing anything, create an issue to discuss your idea. This only applies to big changes and new features.
- Fork it ( https://github.com/fnando/coupons/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request