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, 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
endYou 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
endA 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
endThe 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
endTo 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
endTo 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