achievements 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY.markdown +0 -0
- data/LICENSE +20 -0
- data/README.markdown +103 -0
- data/Rakefile +17 -0
- data/lib/achievements.rb +21 -0
- data/lib/achievements/achievement.rb +23 -0
- data/lib/achievements/achievements_achievement.rb +16 -0
- data/lib/achievements/achievements_agent.rb +27 -0
- data/lib/achievements/achievements_engine.rb +77 -0
- data/lib/achievements/agent.rb +0 -0
- data/lib/achievements/counter.rb +8 -0
- data/lib/achievements/engine.rb +69 -0
- data/lib/achievements/version.rb +4 -0
- data/test/achievements_test.rb +95 -0
- data/test/test_helper.rb +64 -0
- metadata +97 -0
data/HISTORY.markdown
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Michael R. Bernstein
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Achievements
|
2
|
+
|
3
|
+
Achievements is a drop in Achievements/Badging engine designed to work
|
4
|
+
with Ruby, ORM backed web applications.
|
5
|
+
|
6
|
+
Achievements uses Redis for persistence and tries to be as abstract as
|
7
|
+
possible, essentially acting as a counter server with assignable
|
8
|
+
thresholds.
|
9
|
+
|
10
|
+
The application Achievements was developed for uses a User class to
|
11
|
+
instantiate the Engine and an Achievement class to persist the details
|
12
|
+
of the achievements on the application side.
|
13
|
+
|
14
|
+
## Achievements, Briefly
|
15
|
+
|
16
|
+
Here's how you get achievements into your application:
|
17
|
+
|
18
|
+
require 'achievements'
|
19
|
+
|
20
|
+
class Engine
|
21
|
+
# Include the AchievementsEngine module from the Achievements
|
22
|
+
# gem to make your Engine class an AchievementsEngine class.
|
23
|
+
include Achievements::AchievementsEngine
|
24
|
+
|
25
|
+
# Now you can define achievements in this class. One at a time
|
26
|
+
# using the achievement method:
|
27
|
+
|
28
|
+
# One achievement, one level
|
29
|
+
achievement :context1, :one_time, 1
|
30
|
+
|
31
|
+
# Two achievements, one level
|
32
|
+
achievement :context2, :one_time, 1
|
33
|
+
achievement :context2, :three_times, 3
|
34
|
+
|
35
|
+
# One achievement, multiple levels
|
36
|
+
achievement :context3, :multiple_levels, [1, 5, 10]
|
37
|
+
|
38
|
+
# Or all at once using the achievements (plural) method. Right
|
39
|
+
# now this relies on the existence of a class which responds to
|
40
|
+
# id, name, and achievement methods:
|
41
|
+
achievements Achievement.all
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
Here's how you could interact with it in a session with the above
|
46
|
+
class loaded:
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
For the most up to date look at what this library is supposed to do,
|
52
|
+
please refer to the test directory.
|
53
|
+
|
54
|
+
## Details
|
55
|
+
|
56
|
+
### Contexts
|
57
|
+
|
58
|
+
Contexts are categories for achievements. Every time an achievement
|
59
|
+
is triggered, it's "parent" or context counter is also triggered and
|
60
|
+
incremented. This makes it easier to gauge overall participation in
|
61
|
+
the system and makes "score" based calculations less expensive.
|
62
|
+
|
63
|
+
### Achievements
|
64
|
+
|
65
|
+
Achievements are named, contextualized counters with one or more thresholds.
|
66
|
+
|
67
|
+
### Output
|
68
|
+
|
69
|
+
When a threshold isn't crossed, and nothing changes, the engine will
|
70
|
+
return nothing when triggered.
|
71
|
+
|
72
|
+
When a threshold is crossed, the engine will return an array of
|
73
|
+
symbols which correspond to the names of the achievements which have
|
74
|
+
been reached, along with the threshold being crossed. Your application can consume this output as you see
|
75
|
+
fit.
|
76
|
+
|
77
|
+
### Achievement API Compliance
|
78
|
+
|
79
|
+
Your Achievement class must have a name, context, and threshold method
|
80
|
+
in order to adapt to the Engine, in order to be consumed by the Agent
|
81
|
+
class directly.
|
82
|
+
|
83
|
+
## Project Metainfo
|
84
|
+
|
85
|
+
### TODO
|
86
|
+
|
87
|
+
* Better Redis interface methods for making/closing/keeping conneciton
|
88
|
+
|
89
|
+
### Contributing
|
90
|
+
|
91
|
+
0. Do some work on the library, commit
|
92
|
+
1. [Fork][1] Achievements
|
93
|
+
2. Create a topic branch - `git checkout -b my_branch`
|
94
|
+
3. Push to your branch - `git push origin my_branch`
|
95
|
+
4. Create an [Issue][2] with a link to your branch
|
96
|
+
5. That's it!
|
97
|
+
|
98
|
+
[1]: http://help.github.com/forking/
|
99
|
+
[2]: http://github.com/mrb/achievements/issues
|
100
|
+
|
101
|
+
### Author
|
102
|
+
|
103
|
+
Michael R. Bernstein - *michaelrbernstein@gmail.com* - twitter.com/mrb_bk
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift 'lib'
|
2
|
+
#
|
3
|
+
# Publishing
|
4
|
+
#
|
5
|
+
|
6
|
+
desc "Push a new version to Gemcutter"
|
7
|
+
task :publish do
|
8
|
+
require 'achievements/version'
|
9
|
+
|
10
|
+
sh "gem build achievements.gemspec"
|
11
|
+
sh "gem push achievements-#{Achievements::Version}.gem"
|
12
|
+
sh "git tag v#{Achievements::Version}"
|
13
|
+
sh "git push origin v#{Achievements::Version}"
|
14
|
+
sh "git push origin master"
|
15
|
+
sh "git clean -fd"
|
16
|
+
|
17
|
+
end
|
data/lib/achievements.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# An Abstract, Redis-Backed Achievements Engine
|
2
|
+
require 'redis'
|
3
|
+
|
4
|
+
require 'achievements/achievement'
|
5
|
+
require 'achievements/achievements_achievement'
|
6
|
+
require 'achievements/achievements_agent'
|
7
|
+
require 'achievements/achievements_engine'
|
8
|
+
require 'achievements/agent'
|
9
|
+
require 'achievements/counter'
|
10
|
+
require 'achievements/engine'
|
11
|
+
require 'achievements/version'
|
12
|
+
|
13
|
+
module Achievements
|
14
|
+
def self.redis
|
15
|
+
@redis || @redis = Redis.connect
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.engine
|
19
|
+
AchievementsEngine.engine
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Achievements
|
2
|
+
|
3
|
+
# Achievement, basis of counters
|
4
|
+
class Achievement
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :threshold
|
7
|
+
attr_accessor :context
|
8
|
+
|
9
|
+
# A method needs a context, name, and threshold, in that order.
|
10
|
+
#
|
11
|
+
def initialize(context, name, threshold)
|
12
|
+
@context = context
|
13
|
+
@name = name
|
14
|
+
@threshold = threshold
|
15
|
+
end
|
16
|
+
|
17
|
+
# Convenience to_hash method
|
18
|
+
def to_hash
|
19
|
+
{:name => @name, :threshold => @threshold, :context => @context}
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Achievements
|
2
|
+
module AchievementsAchievement
|
3
|
+
# Convenience methods
|
4
|
+
def self.included(base)
|
5
|
+
base.extend IncludeClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module IncludeClassMethods
|
9
|
+
# Update the threshold for the AchievementEngine achievement
|
10
|
+
# that matches context, achievement, and sets threshold to new_threshold
|
11
|
+
def update_threshold(context, achievement, new_threshold)
|
12
|
+
# find achievement and update threshold
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Achievements
|
2
|
+
module AchievementsAgent
|
3
|
+
# Convenience methods for instantiating an engine and adding achievements
|
4
|
+
def self.included(base)
|
5
|
+
base.extend IncludeClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
# Convenience Class Methods for ActiveRecord::Base like User Classes
|
9
|
+
module IncludeClassMethods
|
10
|
+
def engine_class(engine_class)
|
11
|
+
@engine_class = engine_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def engine
|
15
|
+
@engine_class
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Agent instance methods
|
20
|
+
|
21
|
+
# Agent instance level achievement trigger. Automatically sends
|
22
|
+
# agent id along with context and name to the AchievementEngine
|
23
|
+
def achieve(context,name)
|
24
|
+
self.class.engine.achieve context, @id, name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Achievements
|
2
|
+
module AchievementsEngine
|
3
|
+
def self.engine
|
4
|
+
@engine
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend IncludeClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module IncludeClassMethods
|
12
|
+
# Instantiates the AchievementEngine and sets
|
13
|
+
# the contexts, which instantiate context specific counters. Use
|
14
|
+
# only once.
|
15
|
+
#
|
16
|
+
# make_engine [:context1,:context2]
|
17
|
+
#
|
18
|
+
def make_engine(contexts)
|
19
|
+
@redis = Achievements.redis
|
20
|
+
@engine = Engine.new(@redis)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Convenience method for accessing the current engine instance directly
|
24
|
+
def engine
|
25
|
+
@engine
|
26
|
+
end
|
27
|
+
|
28
|
+
# Convenienve method for accessing redis instance
|
29
|
+
def redis
|
30
|
+
@redis
|
31
|
+
end
|
32
|
+
|
33
|
+
# Binds an achievement with a specific counter threshold. Use as
|
34
|
+
# many as you'd like.
|
35
|
+
#
|
36
|
+
# bind :context, :name, threshold
|
37
|
+
#
|
38
|
+
def achievement(context, name, threshold)
|
39
|
+
make_engine(context) if !@engine
|
40
|
+
@engine.achievement(context,name,threshold)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Alternately, bind an entire array of achievement objects. To
|
44
|
+
# use this, achievements must respond to the context, name, and
|
45
|
+
# threshold methods.
|
46
|
+
#
|
47
|
+
# For example, when using with rails:
|
48
|
+
#
|
49
|
+
# bind_all Achievement.all
|
50
|
+
#
|
51
|
+
def achievements(object_array)
|
52
|
+
object_array.each do |object|
|
53
|
+
make_engine(context) if !@engine
|
54
|
+
@engine.achievement object.context, object.name, object.threshold
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Trigger a bound achievement method. Since this is a class
|
59
|
+
# level method, you must include the agent id along with the
|
60
|
+
# method call
|
61
|
+
#
|
62
|
+
# trigger agent_id, context, name
|
63
|
+
#
|
64
|
+
def achieve(context, agent_id, name)
|
65
|
+
@engine.achieve context, agent_id, name
|
66
|
+
end
|
67
|
+
|
68
|
+
def achieves(context_name_array)
|
69
|
+
context_name_array.each do |cna|
|
70
|
+
Achievements.engine.achieve cna[0], @id, cna[1]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
File without changes
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Achievements
|
2
|
+
# Triggering multiple at once
|
3
|
+
|
4
|
+
class Engine
|
5
|
+
attr_accessor :achievements
|
6
|
+
attr_accessor :contexts
|
7
|
+
attr_accessor :redis
|
8
|
+
|
9
|
+
def initialize(redis)
|
10
|
+
@contexts = []
|
11
|
+
@redis = redis
|
12
|
+
@achievements = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def achievement(context,name,threshold)
|
16
|
+
@contexts << context if !@contexts.include?(context)
|
17
|
+
if achievement = Achievement.new(context,name,threshold)
|
18
|
+
[threshold].flatten.each do |thresh|
|
19
|
+
@redis.sadd "#{context}:#{name}:threshold", thresh.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# The trigger method accepts:
|
25
|
+
# context, agent_id, name
|
26
|
+
#
|
27
|
+
# And returns:
|
28
|
+
# context, name, threshold
|
29
|
+
def achieve(context, agent_id, name)
|
30
|
+
achieved = []
|
31
|
+
|
32
|
+
# Increment parent counter
|
33
|
+
counter = Counter.make(context,agent_id,"parent")
|
34
|
+
incr counter
|
35
|
+
|
36
|
+
# Increment child counter
|
37
|
+
counter = Counter.make(context,agent_id,name)
|
38
|
+
result = incr counter
|
39
|
+
|
40
|
+
# Check Threshold
|
41
|
+
|
42
|
+
if @redis.sismember("#{context}:#{name}:threshold", result) == true
|
43
|
+
achieved << [context,name, result.to_s]
|
44
|
+
return achieved
|
45
|
+
else
|
46
|
+
return []
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
# incr key
|
52
|
+
def incr(counter)
|
53
|
+
@redis.incr counter
|
54
|
+
end
|
55
|
+
|
56
|
+
# decr key
|
57
|
+
def decr(counter)
|
58
|
+
@redis.decr counter
|
59
|
+
end
|
60
|
+
|
61
|
+
def deactiveate(counter)
|
62
|
+
@redis.set counter, "ACHIEVED"
|
63
|
+
end
|
64
|
+
|
65
|
+
## Class Methods
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
context "Achievement Restructure Test" do
|
4
|
+
|
5
|
+
setup do
|
6
|
+
# Flush redis before each test
|
7
|
+
Achievements.redis.flushall
|
8
|
+
|
9
|
+
# A sample engine class that demonstrates how to use the
|
10
|
+
# AchievementsEngine include to instantiate the engine, make
|
11
|
+
# achievements, etc.
|
12
|
+
class Engine
|
13
|
+
include Achievements::AchievementsEngine
|
14
|
+
|
15
|
+
# One achievement, one level
|
16
|
+
achievement :context1, :one_time, 1
|
17
|
+
|
18
|
+
# Two achievements, one level
|
19
|
+
achievement :context2, :one_time, 1
|
20
|
+
achievement :context2, :three_times, 3
|
21
|
+
|
22
|
+
# One achievement, multiple levels
|
23
|
+
achievement :context3, :multiple_levels, [1, 5, 10]
|
24
|
+
end
|
25
|
+
|
26
|
+
# A sample agent class that demonstrates how to use the
|
27
|
+
# AchievementsAgent include to achieve directly from a user
|
28
|
+
# class. User class must have an id attribute to comply with the API.
|
29
|
+
class User
|
30
|
+
attr_accessor :id
|
31
|
+
include Achievements::AchievementsAgent
|
32
|
+
engine_class Engine
|
33
|
+
|
34
|
+
def initialize(id)
|
35
|
+
@id = id
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Make a new user with an ID different from the one used in most tests
|
40
|
+
@user = User.new(10)
|
41
|
+
|
42
|
+
# For testing purposes, access the engine and redis directly
|
43
|
+
@engine = Engine.engine
|
44
|
+
@redis = Engine.redis
|
45
|
+
end
|
46
|
+
|
47
|
+
test "Engine gets assigned appropriate contexts" do
|
48
|
+
assert_equal @engine.contexts, [:context1,:context2,:context3]
|
49
|
+
end
|
50
|
+
|
51
|
+
test "Contexts get threshold sets" do
|
52
|
+
assert_equal @redis.smembers("context1:one_time:threshold"), ["1"]
|
53
|
+
assert_equal @redis.smembers("context2:one_time:threshold"), ["1"]
|
54
|
+
assert_equal @redis.smembers("context2:three_times:threshold"), ["3"]
|
55
|
+
assert_equal @redis.smembers("context3:multiple_levels:threshold").sort, ["1","10","5"]
|
56
|
+
end
|
57
|
+
|
58
|
+
test "Trigger should increment parent (context) and child counter" do
|
59
|
+
@engine.achieve(:context1,1,:one_time)
|
60
|
+
assert_equal @redis.get("context1:agent:1:parent"), "1"
|
61
|
+
assert_equal @redis.get("context1:agent:1:one_time"), "1"
|
62
|
+
end
|
63
|
+
|
64
|
+
test "Trigger should return crossed threshold" do
|
65
|
+
response = @engine.achieve(:context1, 1,:one_time)
|
66
|
+
assert_equal [[:context1,:one_time, "1"]], response
|
67
|
+
end
|
68
|
+
|
69
|
+
test "Trigger with threshold higher than 1" do
|
70
|
+
@engine.achieve(:context2, 1, :three_times)
|
71
|
+
@engine.achieve(:context2, 1, :three_times)
|
72
|
+
result = @engine.achieve(:context2, 1, :three_times)
|
73
|
+
assert_equal [[:context2,:three_times, "3"]], result
|
74
|
+
end
|
75
|
+
|
76
|
+
test "Multiple thresholds" do
|
77
|
+
results = []
|
78
|
+
10.times do
|
79
|
+
results << @engine.achieve(:context3,1,:multiple_levels)
|
80
|
+
end
|
81
|
+
assert_equal results, [[[:context3, :multiple_levels, "1"]], [], [], [], [[:context3, :multiple_levels, "5"]], [], [], [], [], [[:context3, :multiple_levels, "10"]]]
|
82
|
+
end
|
83
|
+
|
84
|
+
test "Achieving from the agent class" do
|
85
|
+
response = @user.achieve(:context1,:one_time)
|
86
|
+
assert_equal response, [[:context1,:one_time,"1"]]
|
87
|
+
end
|
88
|
+
|
89
|
+
test "Achieving after threshold returns empty from agent and engine class" do
|
90
|
+
@user.achieve(:context1,:one_time)
|
91
|
+
assert_equal @user.achieve(:context1,:one_time), []
|
92
|
+
@engine.achieve(:context1,1,:one_time)
|
93
|
+
assert_equal @engine.achieve(:context1,1,:one_time),[]
|
94
|
+
end
|
95
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
2
|
+
$LOAD_PATH.unshift dir + '/../lib'
|
3
|
+
$TESTING = true
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'achievements'
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'leftright'
|
10
|
+
rescue LoadError
|
11
|
+
end
|
12
|
+
|
13
|
+
# Thanks, Resque!
|
14
|
+
# http://github.com/defunkt/resque
|
15
|
+
if !system("which redis-server")
|
16
|
+
puts '', "** can't find `redis-server` in your path"
|
17
|
+
abort ''
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# start our own redis when the tests start,
|
22
|
+
# kill it when they end
|
23
|
+
#
|
24
|
+
|
25
|
+
at_exit do
|
26
|
+
next if $!
|
27
|
+
|
28
|
+
if defined?(MiniTest)
|
29
|
+
exit_code = MiniTest::Unit.new.run(ARGV)
|
30
|
+
else
|
31
|
+
exit_code = Test::Unit::AutoRunner.run
|
32
|
+
end
|
33
|
+
|
34
|
+
pid = `ps -A -o pid,command | grep [r]edis-test`.split(" ")[0]
|
35
|
+
puts "Killing test redis server..."
|
36
|
+
`rm -f #{dir}/dump.rdb`
|
37
|
+
Process.kill("KILL", pid.to_i)
|
38
|
+
exit exit_code
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "Starting redis for testing at localhost:9736..."
|
42
|
+
`redis-server #{dir}/redis-test.conf`
|
43
|
+
# AchievementEngine.redis = 'localhost:9736'
|
44
|
+
|
45
|
+
##
|
46
|
+
# test/spec/mini 3
|
47
|
+
# http://gist.github.com/25455
|
48
|
+
# chris@ozmm.org
|
49
|
+
#
|
50
|
+
def context(*args, &block)
|
51
|
+
return super unless (name = args.first) && block
|
52
|
+
require 'test/unit'
|
53
|
+
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
|
54
|
+
def self.test(name, &block)
|
55
|
+
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
|
56
|
+
end
|
57
|
+
def self.xtest(*args) end
|
58
|
+
def self.setup(&block) define_method(:setup, &block) end
|
59
|
+
def self.teardown(&block) define_method(:teardown, &block) end
|
60
|
+
end
|
61
|
+
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
|
62
|
+
klass.class_eval &block
|
63
|
+
end
|
64
|
+
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: achievements
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Michael R. Bernstein
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-02 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: redis
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 25
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 0
|
33
|
+
- 11
|
34
|
+
version: 2.0.11
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
description: " Achievements is an abstract, Redis-backed, counter based achievements engine\n designed to be included in Model classes in web applications.\n\n Achievements lets you track and persist user actions with a simple bind and\n trigger design pattern.\n"
|
38
|
+
email: michaelrbernstein@gmail.com
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
44
|
+
- LICENSE
|
45
|
+
- README.markdown
|
46
|
+
files:
|
47
|
+
- README.markdown
|
48
|
+
- Rakefile
|
49
|
+
- LICENSE
|
50
|
+
- HISTORY.markdown
|
51
|
+
- lib/achievements/counter.rb
|
52
|
+
- lib/achievements/achievements_engine.rb
|
53
|
+
- lib/achievements/achievements_achievement.rb
|
54
|
+
- lib/achievements/achievements_agent.rb
|
55
|
+
- lib/achievements/version.rb
|
56
|
+
- lib/achievements/agent.rb
|
57
|
+
- lib/achievements/engine.rb
|
58
|
+
- lib/achievements/achievement.rb
|
59
|
+
- lib/achievements.rb
|
60
|
+
- test/achievements_test.rb
|
61
|
+
- test/test_helper.rb
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/mrb/achievements
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --charset=UTF-8
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.3.7
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Achievements adds Redis-backed achievments
|
96
|
+
test_files: []
|
97
|
+
|