Commit 4ae41f61 authored by surma Lodur's avatar surma Lodur

Rewards und Quests können ausgelöst werden

parent d6da8546
......@@ -29,12 +29,16 @@ gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.2'
# Pagination Plugin
gem 'kaminari'
group :doc do
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', require: false
end
group :development do
gem 'database_cleaner'
gem 'rails-erd'
gem 'ffaker'
gem 'factory_girl_rails', :require => false
......
......@@ -56,6 +56,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.7.0)
database_cleaner (1.2.0)
devise (3.2.2)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
......@@ -88,6 +89,9 @@ GEM
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.8.1)
kaminari (0.15.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
libv8 (3.16.14.3)
listen (2.5.0)
celluloid (>= 0.15.2)
......@@ -199,6 +203,7 @@ PLATFORMS
DEPENDENCIES
bootstrap-sass
coffee-rails (~> 4.0.0)
database_cleaner
devise
factory_girl_rails
ffaker
......@@ -207,6 +212,7 @@ DEPENDENCIES
guard-rspec
jbuilder (~> 1.2)
jquery-rails
kaminari
rails (= 4.0.2)
rails-erd
rspec-rails
......
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
#
notification(:tmux , {
})
guard(
:rspec
) do
......@@ -24,3 +27,9 @@ guard(
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end
guard :bundler do
watch('Gemfile')
# Uncomment next line if your Gemfile contains the `gemspec' command.
# watch(/^.+\.gemspec/)
end
......@@ -12,7 +12,11 @@ class ChroniclesController < ApplicationController
def set_chronicle
@chronicle = (Earning.all + Redemption.all).sort do |a,b|
a.created_at <=> b.created_at
b.created_at <=> a.created_at
end
@chronicle = Kaminari.paginate_array(
@chronicle
).page(params[:page]).per(20)
end
end
......@@ -6,7 +6,9 @@ class EarningsController < ApplicationController
# TODO user should be set
def cancel
@earning.cancel!(current_hacker)
if @earning.cancel!(current_hacker)
flash[:warning] = "#{@earning.points} storniert"
end
redirect_to :back
end
......
......@@ -42,7 +42,7 @@ class HackersController < ApplicationController
def set_chronicle
@chronicle = (@hacker.earnings.all + @hacker.redemptions.all).sort do |a,b|
a.created_at <=> b.created_at
b.created_at <=> a.created_at
end
end
end
......@@ -12,7 +12,12 @@ class QuestsController < ApplicationController
######################
def solve
@quest.solve(hacker, current_user)
if @quest.solve(@hacker, current_hacker)
flash[:success] = 'Quest abgeschloßen'
else
flash[:error] = 'Quest konnte nicht abgeschloßen werden'
end
redirect_to :back
end
......@@ -23,11 +28,12 @@ class QuestsController < ApplicationController
protected
def set_quest
@quest = Quest.find(params[:id])
@quest = Quest.find(params[:quest])
end # #set_quest
# Da Nested wird der Hacker hier mit ID übermittelt
def set_hacker
@hacker = Hacker.find(params[:hacker_id])
@hacker = Hacker.find(params[:id])
end # #set_hacker
end
......@@ -3,7 +3,9 @@ class RedemptionsController < ApplicationController
before_filter :set_redemption, :only => [:cancel]
def cancel
@redemption.cancel!
if @redemption.cancel!
flash[:warning] = "#{@redemption.points} storniert"
end
redirect_to :back
end
......
# encoding: UTF-8
#
class RewardsController < ApplicationController
before_filter :set_hacker, :only => [:earn]
before_filter :set_reward, :only => [:earn]
######################
## Member Methods ##
######################
def earn
redemption = @reward.earn(@hacker, current_hacker)
if redemption.valid?
flash[:success] = 'Reward eingelöst'
else
flash[:error] = "Reward konnte nicht eingelöst werden\n"
flash[:error] << redemption.errors.full_messages * "\n"
end
redirect_to :back
end
#########################
## Protected Methods ##
#########################
protected
def set_reward
@reward = Reward.find(params[:reward])
end # #set_quest
# Da Nested wird der Hacker hier mit ID übermittelt
def set_hacker
@hacker = Hacker.find(params[:id])
end # #set_hacker
end
......@@ -6,11 +6,15 @@ module ApplicationHelper
"http://gravatar.com/avatar/#{hash}?s=#{size}"
end
def login_or_username
def li_login_or_userpanel
if current_hacker.blank?
link_to 'Einloggen', new_hacker_session_path
content_tag(:li, link_to('Einloggen', new_hacker_session_path))
else
link_to current_hacker.nickname, current_hacker
concat(
content_tag(:li, link_to(current_hacker.nickname, current_hacker))
)
content_tag(:li, link_to('Ausloggen', destroy_hacker_session_path, :method => :delete))
end
end
end
......@@ -9,28 +9,50 @@ class Hacker < ActiveRecord::Base
has_many :earnings, :foreign_key => :hacker_id
has_many :redemptions, :foreign_key => :hacker_id
has_many :earning_cancels, :through => :earnings, :source => :cancel
has_many :redemption_cancels, :through => :redemptions, :source => :cancel
######################
## Instance Methods ##
######################
#
def redemptions_without_cancel
return redemptions.
where(
Redemption.arel_table[:id].not_in(
self.redemption_cancels.map(&:subject_id)
)
)
end
# Summe aller jemals eingelösten Punkte
def all_redeemed_points
return redemptions.where("id not in (select subject_id from cancels where subject_type = 'Redemption')").sum('points')
return redemptions_without_cancel.sum('points')
end
# Summe aller in den letzten 90 Tagen verdienten Punkte
def recent_earned_points
return earnings.where("id not in (select subject_id from cancels where subject_type = 'Earning')").
where('created_at >= ?', Date.today - 90).sum('points')
def earnings_without_cancel
return earnings.
where(
Earning.arel_table[:id].not_in(
self.earning_cancels.map(&:subject_id)
)
)
end
# Summe aller jemals verdienten Punkte,
# unabhängig davon, ob diese bereits
# eingelöst wurden oder nicht
def all_earned_points
return earnings.sum('points')
return earnings_without_cancel.sum('points')
end
# Summe aller in den letzten 90 Tagen verdienten Punkte
def recent_earned_points
return earnings_without_cancel.
where('created_at >= ?', Date.today - 90).sum('points')
end
# Verbleibendes Guthaben, d.h. alle jemals verdienten Punkte
# abzüglich aller jemals eingelösten Punkte,
......
......@@ -12,7 +12,7 @@ class Quest < ActiveRecord::Base
hacker.earnings.create({
:user => user,
:points => self.points,
:chronicle_text => self.description
:chronicle_text => self.title
})
end # #solve
end
......@@ -7,4 +7,13 @@ class Reward < ActiveRecord::Base
validates :points, :title, :presence => true
validates :points, :numericality => {:greater_than => 0}
def earn(hacker, user = nil)
hacker.redemptions.create({
:user => user,
:points => self.points,
:chronicle_text => self.title,
:reward => self
})
end
end
<div class="list-group-item row list-group-item-success">
<span class="col-md-1">
<%= image_tag gravatar_url_for(entry.hacker.email, 24) %>
</span>
<span class="col-md-2">
+<%= entry.points %>
</span>
......
<div class="list-group-item list-group-item-warning row">
<span class="col-md-1">
<%= image_tag gravatar_url_for(entry.hacker.email, 24) %>
</span>
<span class="col-md-1 col-md-offset-1">
+<%= entry.points %>
</span>
......
<%= paginate @chronicle %>
<%= render :partial => 'hackers/chronicle', :locals => {:chronicle => @chronicle} %>
<%= paginate @chronicle %>
......@@ -3,7 +3,8 @@
<%= render({
:partial => 'hackers/quests/show',
:collection => grouped_quests.compact,
:as => :quest
:as => :quest,
:locals => {:hacker => hacker}
}) %>
</div>
<% end %>
......@@ -3,7 +3,8 @@
<%= render({
:partial => 'hackers/rewards/show',
:collection => grouped_rewards.compact,
:as => :reward
:as => :reward,
:locals => {:hacker => hacker}
}) %>
</div>
<% end %>
<div class="list-group-item list-group-item-danger row">
<span class="col-md-1">
</span>
<span class="col-md-2">
<%= entry.points %>
</span>
......
<div class="list-group-item list-group-item-danger row">
<span class="col-md-1">
</span>
<span class="col-md-1 col-md-offset-1">
<%= entry.points %>
</span>
......
<div class="btn btn-default btn-lg col-md-2 quest-button">
<div class="row">
<div class="col-xs-2">
<!-- TODO: should be replaced with paperclip gem -->
<span class="glyphicon <%= quest.image_url %>"> </span>
</div>
<div class="col-xs-7">
<%= quest.title %>
</div>
<div class="col-xs-2">
<%= quest.points %>
<a
data-method="post" rel="nofollow"
href="<%= solve_quest_path(:hacker => hacker, :quest => quest) %>"
>
<div class="btn btn-default btn-lg col-md-2 quest-button">
<div class="row">
<div class="col-xs-2">
<!-- TODO: should be replaced with paperclip gem -->
<span class="glyphicon <%= quest.image_url %>"> </span>
</div>
<div class="col-xs-7">
<%= quest.title %>
</div>
<div class="col-xs-2">
<%= quest.points %>
</div>
</div>
</div>
</div>
</a>
<div class="btn btn-default col-md-3 reward-button">
<div class="row">
<div class="col-xs-2">
<!-- TODO: should be replaced with paperclip gem -->
<span class="glyphicon <%= reward.image_url %>"> </span>
</div>
<div class="col-xs-7">
<%= reward.title %>
</div>
<div class="col-xs-2">
<%= reward.points %>
<a
data-method="post" rel="nofollow"
href="<%= earn_reward_path(:hacker => hacker, :reward => reward) %>"
>
<div class="btn btn-default col-md-3 reward-button">
<div class="row">
<div class="col-xs-2">
<!-- TODO: should be replaced with paperclip gem -->
<span class="glyphicon <%= reward.image_url %>"> </span>
</div>
<div class="col-xs-7">
<%= reward.title %>
</div>
<div class="col-xs-2">
<%= reward.points %>
</div>
</div>
</div>
</div>
</a>
......@@ -33,12 +33,12 @@
<h2>Punkte erarbeitet</h2>
<div class="container-fluid">
<%= render :partial => 'quests', :locals => {:quests => @quests} %>
<%= render :partial => 'quests', :locals => {:quests => @quests, :hacker => @hacker} %>
</div>
<h2>Punkte einlösen</h2>
<div class="container-fluid">
<%= render :partial => 'rewards', :locals => {:rewards => @rewards} %>
<%= render :partial => 'rewards', :locals => {:rewards => @rewards, :hacker => @hacker} %>
</div>
<h2>Verlauf</h2>
......
......@@ -13,6 +13,15 @@
</div>
</nav>
<div class="container">
<% if flash[:success] %>
<div class="alert alert-success"><%= flash[:success] %></div>
<% end %>
<% if flash[:error] %>
<div class="alert alert-danger"><%= flash[:error] %></div>
<% end %>
<% if flash[:warning] %>
<div class="alert alert-warning"><%= flash[:warning] %></div>
<% end %>
<%= yield %>
</div>
......@@ -21,7 +30,7 @@
<nav class="navbar navbar-default navbar-fixed-bottom" role="navigation">
<div class="container">
<ul class="nav navbar-nav">
<li><%= login_or_username %> </li>
<%= li_login_or_userpanel %>
<li class="divider"></li>
<li><%= link_to 'Historie', chronicle_path %></li>
</ul>
......
......@@ -9,7 +9,13 @@ SpacePunkte::Application.routes.draw do
member do
post :solve
end
end
end # quests
resources :rewards, :only => [] do
member do
post :earn
end
end # rewards
end
end
......
# encoding: UTF-8
require 'spec_helper'
describe QuestsController do
let(:hacker) do
FactoryGirl.create(:hacker)
end
let(:user) do
FactoryGirl.create(:hacker)
end
let(:quest) do
FactoryGirl.create(:quest)
end
before do
request.env['HTTP_REFERER'] = 'test.de'
end
context 'not logged in' do
describe 'POST solve' do
it 'should solve a quest' do
hacker.earnings.should be_blank
post :solve, :id => hacker.id, :quest => quest.id
hacker.reload.should have(1).earnings
hacker.earnings.last.user.should be_blank
end
end # POST solve
end # not logged in
context 'logged in' do
before do
controller.stub(:current_hacker).and_return(user)
end
describe 'POST solve' do
it 'should solve a quest' do
hacker.earnings.should be_blank
post :solve, :id => hacker.id, :quest => quest.id
hacker.reload.should have(1).earnings
hacker.earnings.last.user.should be_eql(user)
end
end # POST solve
end # logged in
end
# encoding: UTF-8
require 'spec_helper'
describe RewardsController do
let(:hacker) do
FactoryGirl.create(:hacker)
end
let(:user) do
FactoryGirl.create(:hacker)
end
let(:reward) do
FactoryGirl.create(:reward)
end
before do
request.env['HTTP_REFERER'] = 'test.de'
end
context 'not logged in' do
describe 'POST earn' do
it 'should earn a reward' do
hacker.redemptions.should be_blank
post :earn, :id => hacker.id, :reward => reward.id
response.should be_redirect
hacker.reload.should have(1).redemptions
hacker.redemptions.last.user.should be_blank
end
end # POST earn
end # not logged in
context 'logged in' do
before do
controller.stub(:current_hacker).and_return(user)
end
describe 'POST earn' do
it 'should earn a reward' do
hacker.redemptions.should be_blank
post :earn, :id => hacker.id, :reward => reward.id
response.should be_redirect
hacker.reload.should have(1).redemptions
hacker.redemptions.last.user.should be_eql(user)
end
end # POST earn
end # logged in
end
require 'spec_helper'
describe Reward do
pending "add some examples to (or delete) #{__FILE__}"
let(:hacker) do
FactoryGirl.create(:hacker)
end
let(:user) do
FactoryGirl.create(:user)
end
subject do
FactoryGirl.create(:reward)
end
it 'should earn a reward' do
hacker.should have(0).redemptions
subject.earn(hacker)
hacker.reload.should have(1).redemptions
end
end
......@@ -7,6 +7,9 @@ require 'rspec/autorun'
require 'factory_girl_rails'
require 'ffaker'
require 'database_cleaner'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
......@@ -43,4 +46,18 @@ RSpec.configure do |config|
# --seed 1234
config.order = "random"
config.include Devise::TestHelpers, :type => :controller
DatabaseCleaner.strategy = :transaction
config.before(:all) do
DatabaseCleaner.clean_with :truncation
end
config.before do
DatabaseCleaner.start # usually this is called in setup of a test
end
config.after do
DatabaseCleaner.clean
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment