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

Rewards und Quests können ausgelöst werden

parent d6da8546
...@@ -29,12 +29,16 @@ gem 'turbolinks' ...@@ -29,12 +29,16 @@ gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.2' gem 'jbuilder', '~> 1.2'
# Pagination Plugin
gem 'kaminari'
group :doc do group :doc do
# bundle exec rake doc:rails generates the API under doc/api. # bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', require: false gem 'sdoc', require: false
end end
group :development do group :development do
gem 'database_cleaner'
gem 'rails-erd' gem 'rails-erd'
gem 'ffaker' gem 'ffaker'
gem 'factory_girl_rails', :require => false gem 'factory_girl_rails', :require => false
......
...@@ -56,6 +56,7 @@ GEM ...@@ -56,6 +56,7 @@ GEM
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.7.0) coffee-script-source (1.7.0)
database_cleaner (1.2.0)
devise (3.2.2) devise (3.2.2)
bcrypt-ruby (~> 3.0) bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
...@@ -88,6 +89,9 @@ GEM ...@@ -88,6 +89,9 @@ GEM
railties (>= 3.0, < 5.0) railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (1.8.1) json (1.8.1)
kaminari (0.15.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
libv8 (3.16.14.3) libv8 (3.16.14.3)
listen (2.5.0) listen (2.5.0)
celluloid (>= 0.15.2) celluloid (>= 0.15.2)
...@@ -199,6 +203,7 @@ PLATFORMS ...@@ -199,6 +203,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
bootstrap-sass bootstrap-sass
coffee-rails (~> 4.0.0) coffee-rails (~> 4.0.0)
database_cleaner
devise devise
factory_girl_rails factory_girl_rails
ffaker ffaker
...@@ -207,6 +212,7 @@ DEPENDENCIES ...@@ -207,6 +212,7 @@ DEPENDENCIES
guard-rspec guard-rspec
jbuilder (~> 1.2) jbuilder (~> 1.2)
jquery-rails jquery-rails
kaminari
rails (= 4.0.2) rails (= 4.0.2)
rails-erd rails-erd
rspec-rails rspec-rails
......
# A sample Guardfile # A sample Guardfile
# More info at https://github.com/guard/guard#readme # More info at https://github.com/guard/guard#readme
#
notification(:tmux , {
})
guard( guard(
:rspec :rspec
) do ) do
...@@ -24,3 +27,9 @@ guard( ...@@ -24,3 +27,9 @@ guard(
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end 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 ...@@ -12,7 +12,11 @@ class ChroniclesController < ApplicationController
def set_chronicle def set_chronicle
@chronicle = (Earning.all + Redemption.all).sort do |a,b| @chronicle = (Earning.all + Redemption.all).sort do |a,b|
a.created_at <=> b.created_at b.created_at <=> a.created_at
end end
@chronicle = Kaminari.paginate_array(
@chronicle
).page(params[:page]).per(20)
end end
end end
...@@ -6,7 +6,9 @@ class EarningsController < ApplicationController ...@@ -6,7 +6,9 @@ class EarningsController < ApplicationController
# TODO user should be set # TODO user should be set
def cancel def cancel
@earning.cancel!(current_hacker) if @earning.cancel!(current_hacker)
flash[:warning] = "#{@earning.points} storniert"
end
redirect_to :back redirect_to :back
end end
......
...@@ -42,7 +42,7 @@ class HackersController < ApplicationController ...@@ -42,7 +42,7 @@ class HackersController < ApplicationController
def set_chronicle def set_chronicle
@chronicle = (@hacker.earnings.all + @hacker.redemptions.all).sort do |a,b| @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 end
end end
...@@ -12,7 +12,12 @@ class QuestsController < ApplicationController ...@@ -12,7 +12,12 @@ class QuestsController < ApplicationController
###################### ######################
def solve 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 end
...@@ -23,11 +28,12 @@ class QuestsController < ApplicationController ...@@ -23,11 +28,12 @@ class QuestsController < ApplicationController
protected protected
def set_quest def set_quest
@quest = Quest.find(params[:id]) @quest = Quest.find(params[:quest])
end # #set_quest end # #set_quest
# Da Nested wird der Hacker hier mit ID übermittelt
def set_hacker def set_hacker
@hacker = Hacker.find(params[:hacker_id]) @hacker = Hacker.find(params[:id])
end # #set_hacker end # #set_hacker
end end
...@@ -3,7 +3,9 @@ class RedemptionsController < ApplicationController ...@@ -3,7 +3,9 @@ class RedemptionsController < ApplicationController
before_filter :set_redemption, :only => [:cancel] before_filter :set_redemption, :only => [:cancel]
def cancel def cancel
@redemption.cancel! if @redemption.cancel!
flash[:warning] = "#{@redemption.points} storniert"
end
redirect_to :back redirect_to :back
end 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 ...@@ -6,11 +6,15 @@ module ApplicationHelper
"http://gravatar.com/avatar/#{hash}?s=#{size}" "http://gravatar.com/avatar/#{hash}?s=#{size}"
end end
def login_or_username def li_login_or_userpanel
if current_hacker.blank? if current_hacker.blank?
link_to 'Einloggen', new_hacker_session_path content_tag(:li, link_to('Einloggen', new_hacker_session_path))
else 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 end
end end
...@@ -9,28 +9,50 @@ class Hacker < ActiveRecord::Base ...@@ -9,28 +9,50 @@ class Hacker < ActiveRecord::Base
has_many :earnings, :foreign_key => :hacker_id has_many :earnings, :foreign_key => :hacker_id
has_many :redemptions, :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 ## ## 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 # Summe aller jemals eingelösten Punkte
def all_redeemed_points 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 end
# Summe aller in den letzten 90 Tagen verdienten Punkte def earnings_without_cancel
def recent_earned_points return earnings.
return earnings.where("id not in (select subject_id from cancels where subject_type = 'Earning')"). where(
where('created_at >= ?', Date.today - 90).sum('points') Earning.arel_table[:id].not_in(
self.earning_cancels.map(&:subject_id)
)
)
end end
# Summe aller jemals verdienten Punkte, # Summe aller jemals verdienten Punkte,
# unabhängig davon, ob diese bereits # unabhängig davon, ob diese bereits
# eingelöst wurden oder nicht # eingelöst wurden oder nicht
def all_earned_points def all_earned_points
return earnings.sum('points') return earnings_without_cancel.sum('points')
end 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 # Verbleibendes Guthaben, d.h. alle jemals verdienten Punkte
# abzüglich aller jemals eingelösten Punkte, # abzüglich aller jemals eingelösten Punkte,
......
...@@ -12,7 +12,7 @@ class Quest < ActiveRecord::Base ...@@ -12,7 +12,7 @@ class Quest < ActiveRecord::Base
hacker.earnings.create({ hacker.earnings.create({
:user => user, :user => user,
:points => self.points, :points => self.points,
:chronicle_text => self.description :chronicle_text => self.title
}) })
end # #solve end # #solve
end end
...@@ -7,4 +7,13 @@ class Reward < ActiveRecord::Base ...@@ -7,4 +7,13 @@ class Reward < ActiveRecord::Base
validates :points, :title, :presence => true validates :points, :title, :presence => true
validates :points, :numericality => {:greater_than => 0} 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 end
<div class="list-group-item row list-group-item-success"> <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"> <span class="col-md-2">
+<%= entry.points %> +<%= entry.points %>
</span> </span>
......
<div class="list-group-item list-group-item-warning row"> <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"> <span class="col-md-1 col-md-offset-1">
+<%= entry.points %> +<%= entry.points %>
</span> </span>
......
<%= paginate @chronicle %>
<%= render :partial => 'hackers/chronicle', :locals => {:chronicle => @chronicle} %> <%= render :partial => 'hackers/chronicle', :locals => {:chronicle => @chronicle} %>
<%= paginate @chronicle %>
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
<%= render({ <%= render({
:partial => 'hackers/quests/show', :partial => 'hackers/quests/show',
:collection => grouped_quests.compact, :collection => grouped_quests.compact,
:as => :quest :as => :quest,
:locals => {:hacker => hacker}
}) %> }) %>
</div> </div>
<% end %> <% end %>
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
<%= render({ <%= render({
:partial => 'hackers/rewards/show', :partial => 'hackers/rewards/show',
:collection => grouped_rewards.compact, :collection => grouped_rewards.compact,
:as => :reward :as => :reward,
:locals => {:hacker => hacker}
}) %> }) %>
</div> </div>
<% end %> <% end %>
<div class="list-group-item list-group-item-danger row"> <div class="list-group-item list-group-item-danger row">
<span class="col-md-1">
</span>
<span class="col-md-2"> <span class="col-md-2">
<%= entry.points %> <%= entry.points %>
</span> </span>
......
<div class="list-group-item list-group-item-danger row"> <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"> <span class="col-md-1 col-md-offset-1">
<%= entry.points %> <%= entry.points %>
</span> </span>
......
<div class="btn btn-default btn-lg col-md-2 quest-button"> <a
<div class="row"> data-method="post" rel="nofollow"
<div class="col-xs-2"> href="<%= solve_quest_path(:hacker => hacker, :quest => quest) %>"
<!-- TODO: should be replaced with paperclip gem --> >
<span class="glyphicon <%= quest.image_url %>"> </span> <div class="btn btn-default btn-lg col-md-2 quest-button">
</div> <div class="row">
<div class="col-xs-7"> <div class="col-xs-2">
<%= quest.title %> <!-- TODO: should be replaced with paperclip gem -->
</div> <span class="glyphicon <%= quest.image_url %>"> </span>
<div class="col-xs-2"> </div>
<%= quest.points %> <div class="col-xs-7">
<%= quest.title %>
</div>
<div class="col-xs-2">
<%= quest.points %>
</div>
</div> </div>
</div> </div>
</div> </a>
<div class="btn btn-default col-md-3 reward-button"> <a
<div class="row"> data-method="post" rel="nofollow"
<div class="col-xs-2"> href="<%= earn_reward_path(:hacker => hacker, :reward => reward) %>"
<!-- TODO: should be replaced with paperclip gem --> >
<span class="glyphicon <%= reward.image_url %>"> </span> <div class="btn btn-default col-md-3 reward-button">
</div> <div class="row">
<div class="col-xs-7"> <div class="col-xs-2">
<%= reward.title %> <!-- TODO: should be replaced with paperclip gem -->
</div> <span class="glyphicon <%= reward.image_url %>"> </span>
<div class="col-xs-2"> </div>
<%= reward.points %> <div class="col-xs-7">
<%= reward.title %>
</div>
<div class="col-xs-2">
<%= reward.points %>
</div>
</div> </div>
</div> </div>
</div> </a>
...@@ -33,12 +33,12 @@ ...@@ -33,12 +33,12 @@
<h2>Punkte erarbeitet</h2> <h2>Punkte erarbeitet</h2>
<div class="container-fluid"> <div class="container-fluid">
<%= render :partial => 'quests', :locals => {:quests => @quests} %> <%= render :partial => 'quests', :locals => {:quests => @quests, :hacker => @hacker} %>
</div> </div>
<h2>Punkte einlösen</h2> <h2>Punkte einlösen</h2>
<div class="container-fluid"> <div class="container-fluid">
<%= render :partial => 'rewards', :locals => {:rewards => @rewards} %> <%= render :partial => 'rewards', :locals => {:rewards => @rewards, :hacker => @hacker} %>
</div> </div>
<h2>Verlauf</h2> <h2>Verlauf</h2>
......
...@@ -13,6 +13,15 @@ ...@@ -13,6 +13,15 @@
</div> </div>
</nav> </nav>
<div class="container"> <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 %> <%= yield %>
</div> </div>
...@@ -21,7 +30,7 @@ ...@@ -21,7 +30,7 @@
<nav class="navbar navbar-default navbar-fixed-bottom" role="navigation"> <nav class="navbar navbar-default navbar-fixed-bottom" role="navigation">
<div class="container"> <div class="container">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><%= login_or_username %> </li> <%= li_login_or_userpanel %>
<li class="divider"></li> <li class="divider"></li>
<li><%= link_to 'Historie', chronicle_path %></li> <li><%= link_to 'Historie', chronicle_path %></li>
</ul> </ul>
......
...@@ -9,7 +9,13 @@ SpacePunkte::Application.routes.draw do ...@@ -9,7 +9,13 @@ SpacePunkte::Application.routes.draw do
member do member do
post :solve post :solve
end end
end end # quests
resources :rewards, :only => [] do
member do
post :earn
end
end # rewards
end end
end end
......