scorecard 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5d64b389e1a10a193f8d53fb445000f19b15b3c8
4
+ data.tar.gz: 426aaad02a63b1c11904ff53f4a8f4795f895e25
5
+ SHA512:
6
+ metadata.gz: 1de862ba8ae7f660ec7b39e03d045f676c9f5658f1057d2768562cc764c7f430632bf3be98a91d4189253c92573f8309c36545ed0a3c85b9884dac61b16bf82c
7
+ data.tar.gz: 754029cc38373c33d8a3487093f3988688fbfdeec2b0dfb44e4d3dbdb88acaa01111efcec7756729b212bb89a780a4656d5227b02b19f5f37e5b5e3dd9f5355a
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/internal/db/*.sqlite
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ notifications:
6
+ campfire:
7
+ rooms:
8
+ - secure: "nK5kN3xf43qNUzQ2e+3hCD1Iy/XzqMS1EOm1aDyvbi1nv4xHD7ZkKTRgFAPYGJj/SpuSkIlWbFybYUfeGe9SVrg9/C70vlNmmuNkK2YV106qd5IemB65qzVb35yU7kyF84u5/kAHg8EdWfXsBYxB6KoXJoql1lGCYQMaH0L6Rb8="
9
+ on_success: change
10
+ on_failure: change
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Inspire9 and Pat Allan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Scorecard
2
+
3
+ A Rails engine for tracking points, badges and levels for gamification.
4
+
5
+ For anyone who comes across this, please note this is not yet feature complete, and the API will very likely change.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'scorecard'
12
+
13
+ Don't forget to bundle:
14
+
15
+ $ bundle
16
+
17
+ Then, add the migrations to your app and update your database accordingly:
18
+
19
+ $ rake scorecard:install:migrations db:migrate
20
+
21
+ ## Rules
22
+
23
+ In an initializer, define the events that points are tied to - with a unique context and the number of points:
24
+
25
+ ```ruby
26
+ Scorecard::PointRule.new :new_post, 50
27
+ ```
28
+
29
+ You can also provide a block with logic for whether to award the points, the maximum number of points allowed to be awarded for this rule, and the timeframe for that limit:
30
+
31
+ ```ruby
32
+ Scorecard::PointRule.new :new_post, 50, limit: 100, timeframe: :day,
33
+ if: lambda { |payload| payload[:user].posts.count <= 1 }
34
+ ```
35
+
36
+ The payload object contains the context plus every option you send through to `score` call (see below).
37
+
38
+ ## Scoring
39
+
40
+ And then, when you want to fire those events, use code much like the following:
41
+
42
+ ```ruby
43
+ Scorecard::Points.score :new_post, gameable: post
44
+ ```
45
+
46
+ This presumes that the user the points are for is a method on the gameable object. If that's not the case, you can pass in a custom user:
47
+
48
+ ```ruby
49
+ Scorecard::Points.score :new_post, gameable: post, user: user
50
+ ```
51
+
52
+ If you're using Sidekiq, you can push the scoring behaviour into a background worker using `score_async` instead. Make sure `scorecard` is listed below `sidekiq` in your `Gemfile`.
53
+
54
+ ```ruby
55
+ Scorecard::Points.score_async :new_post, gameable: post
56
+ ```
57
+
58
+ ## Contributing
59
+
60
+ 1. Fork it
61
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
62
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
63
+ 4. Push to the branch (`git push origin my-new-feature`)
64
+ 5. Create new Pull Request
65
+
66
+ ## Licence
67
+
68
+ Copyright (c) 2013, Scorecard is developed and maintained by [Inspire9](http://inspire9.com), and is released under the open MIT Licence.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
@@ -0,0 +1,25 @@
1
+ class Scorecard::Point < ActiveRecord::Base
2
+ self.table_name = 'scorecard_points'
3
+
4
+ belongs_to :user, polymorphic: true
5
+ belongs_to :gameable, polymorphic: true
6
+
7
+ validates :context, presence: true
8
+ validates :identifier, presence: true, uniqueness: {
9
+ scope: [:context, :user_type, :user_id]
10
+ }
11
+ validates :amount, presence: true
12
+ validates :user, presence: true
13
+
14
+ def self.for_user(context, user)
15
+ where(
16
+ context: context,
17
+ user_id: user.id,
18
+ user_type: user.class.name
19
+ )
20
+ end
21
+
22
+ def self.for_user_in_timeframe(context, user, timeframe)
23
+ for_user(context, user).where(created_at: timeframe)
24
+ end
25
+ end
data/config.ru ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize!
7
+ run Combustion::Application
@@ -0,0 +1,21 @@
1
+ class CreatePoints < ActiveRecord::Migration
2
+ def change
3
+ create_table :scorecard_points do |table|
4
+ table.string :context, null: false
5
+ table.string :identifier, null: false
6
+ table.integer :amount, null: false
7
+ table.string :user_type, null: false
8
+ table.integer :user_id, null: false
9
+ table.string :gameable_type
10
+ table.integer :gameable_id
11
+ table.timestamps
12
+ end
13
+
14
+ add_index :scorecard_points, :context
15
+ add_index :scorecard_points, :identifier
16
+ add_index :scorecard_points, [:user_type, :user_id]
17
+ add_index :scorecard_points, [:gameable_type, :gameable_id]
18
+ add_index :scorecard_points, [:context, :identifier, :user_type, :user_id],
19
+ unique: true, name: :unique_points
20
+ end
21
+ end
data/lib/scorecard.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Scorecard
2
+ def self.configure
3
+ yield self
4
+ end
5
+
6
+ def self.rules
7
+ @rules ||= Scorecard::Rules.new
8
+ end
9
+ end
10
+
11
+ require 'scorecard/engine'
12
+ require 'scorecard/points'
13
+ require 'scorecard/point_rule'
14
+ require 'scorecard/rules'
15
+ require 'scorecard/subscriber'
16
+ require 'scorecard/worker' if defined?(Sidekiq)
@@ -0,0 +1,7 @@
1
+ class Scorecard::Engine < Rails::Engine
2
+ engine_name :scorecard
3
+
4
+ initializer :scorecard do |app|
5
+ Scorecard::Subscriber.attach_to :scorecard
6
+ end
7
+ end
@@ -0,0 +1,46 @@
1
+ class Scorecard::PointRule
2
+ TIMESPANS = [:day, :week, :month, :year]
3
+
4
+ attr_accessor :context, :amount, :options
5
+
6
+ def initialize(context, amount, options = {})
7
+ @context, @amount, @options = context, amount, options
8
+ end
9
+
10
+ def allowed?(payload)
11
+ if limit
12
+ return false unless current_points(payload).sum(:amount) < limit
13
+ elsif timeframe
14
+ return false unless current_points(payload).count.zero?
15
+ end
16
+
17
+ options[:if].nil? || options[:if].call(payload)
18
+ end
19
+
20
+ private
21
+
22
+ def current_points(payload)
23
+ if timeframe.nil?
24
+ Scorecard::Point.for_user(payload[:context], payload[:user])
25
+ else
26
+ Scorecard::Point.for_user_in_timeframe(payload[:context], payload[:user], time_range)
27
+ end
28
+ end
29
+
30
+ def limit
31
+ options[:limit]
32
+ end
33
+
34
+ def timeframe
35
+ options[:timeframe]
36
+ end
37
+
38
+ def time_range
39
+ unless TIMESPANS.include? timeframe
40
+ raise ArgumentError, "Unknown timeframe argument #{timeframe.inspect}"
41
+ end
42
+
43
+ now = Time.zone.now
44
+ now.send("beginning_of_#{timeframe}")..now.send("end_of_#{timeframe}")
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ class Scorecard::Points
2
+ def self.score(context, options)
3
+ ActiveSupport::Notifications.instrument 'points.scorecard',
4
+ options.merge(context: context)
5
+ end
6
+
7
+ def self.score_async(context, options)
8
+ [:gameable, :user].each do |prefix|
9
+ next unless options[prefix]
10
+
11
+ options["#{prefix}_id"] = options[prefix].id
12
+ options["#{prefix}_type"] = options[prefix].class.name
13
+ options.delete prefix
14
+ end
15
+
16
+ Scorecard::Worker.perform_async context, options.stringify_keys
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ class Scorecard::Rules
2
+ attr_reader :point_rules
3
+
4
+ def initialize
5
+ @point_rules = []
6
+ end
7
+
8
+ def add_rule_for_points(context, amount, options = {})
9
+ point_rules << Scorecard::PointRule.new(context, amount, options)
10
+ end
11
+
12
+ def find_rule_for_points(context)
13
+ point_rules.detect { |rule| rule.context == context }
14
+ end
15
+
16
+ def clear
17
+ point_rules.clear
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ class Scorecard::Subscriber
2
+ def self.attach_to(suffix)
3
+ subscriber = new
4
+ subscriber.public_methods(false).each do |event|
5
+ next if event.to_s == 'call'
6
+
7
+ ActiveSupport::Notifications.subscribe "#{event}.#{suffix}", subscriber
8
+ end
9
+ end
10
+
11
+ def call(message, *args)
12
+ method = message.split('.').first
13
+ send method, ActiveSupport::Notifications::Event.new(message, *args)
14
+ end
15
+
16
+ def points(event)
17
+ rule = Scorecard.rules.find_rule_for_points event.payload[:context]
18
+
19
+ event.payload[:amount] ||= rule.amount
20
+ event.payload[:identifier] ||= event.payload[:gameable].id
21
+ event.payload[:user] ||= event.payload[:gameable].user
22
+
23
+ return unless rule && rule.allowed?(event.payload)
24
+
25
+ Scorecard::Point.create(
26
+ event.payload.slice(:context, :amount, :identifier, :user, :gameable)
27
+ )
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ class Scorecard::Worker
2
+ include Sidekiq::Worker
3
+
4
+ def perform(context, options)
5
+ %w(gameable user).each do |prefix|
6
+ next unless options["#{prefix}_type"]
7
+
8
+ klass = options.delete("#{prefix}_type").constantize
9
+ options[prefix] = klass.find options.delete("#{prefix}_id")
10
+ end
11
+
12
+ Scorecard::Points.score context.to_sym, options.symbolize_keys
13
+ end
14
+ end
data/scorecard.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ Gem::Specification.new do |spec|
3
+ spec.name = 'scorecard'
4
+ spec.version = '0.0.1'
5
+ spec.authors = ['Pat Allan']
6
+ spec.email = ['pat@freelancing-gods.com']
7
+ spec.summary = 'Rails Engine for common scorecard patterns'
8
+ spec.description = 'Use an engine to track points, badges and levels in your Rails app.'
9
+ spec.homepage = 'https://github.com/inspire9/scorecard'
10
+ spec.license = 'MIT'
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
14
+ spec.require_paths = ['lib']
15
+
16
+ spec.add_runtime_dependency 'rails', '>= 3.2'
17
+
18
+ spec.add_development_dependency 'combustion', '~> 0.5.1'
19
+ spec.add_development_dependency 'rspec-rails', '~> 2.14.0'
20
+ spec.add_development_dependency 'sidekiq', '~> 2.15'
21
+ spec.add_development_dependency 'sqlite3', '~> 1.3.8'
22
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Scorecard' do
4
+ before :each do
5
+ Scorecard.rules.clear
6
+ end
7
+
8
+ it "stores points for configured behaviour" do
9
+ Scorecard.configure do |config|
10
+ config.rules.add_rule_for_points :new_post, 50
11
+ end
12
+
13
+ user = User.create!
14
+ post = Post.create! user: user
15
+
16
+ Scorecard::Point.where(
17
+ context: 'new_post',
18
+ amount: 50,
19
+ identifier: post.id.to_s,
20
+ user_id: user.id,
21
+ user_type: 'User',
22
+ gameable_id: post.id,
23
+ gameable_type: 'Post'
24
+ ).should_not be_empty
25
+ end
26
+
27
+ it "only stores points when provided logic passes" do
28
+ Scorecard.configure do |config|
29
+ config.rules.add_rule_for_points :new_post, 50,
30
+ if: lambda { |payload| Post.count <= 1 }
31
+ end
32
+
33
+ user = User.create!
34
+ post = Post.create! user: user
35
+ post = Post.create! user: user
36
+
37
+ Scorecard::Point.where(
38
+ context: 'new_post',
39
+ amount: 50
40
+ ).count.should == 1
41
+ end
42
+
43
+ it "does not double-up on points for the same event" do
44
+ Scorecard.configure do |config|
45
+ config.rules.add_rule_for_points :new_post, 50
46
+ end
47
+
48
+ user = User.create!
49
+ post = Post.create! user: user
50
+ Scorecard::Points.score :new_post, gameable: post
51
+
52
+ Scorecard::Point.where(
53
+ context: 'new_post',
54
+ amount: 50,
55
+ identifier: post.id.to_s,
56
+ user_id: user.id,
57
+ user_type: 'User',
58
+ gameable_id: post.id,
59
+ gameable_type: 'Post'
60
+ ).count.should == 1
61
+ end
62
+
63
+ it "respects limit options" do
64
+ Scorecard.configure do |config|
65
+ config.rules.add_rule_for_points :new_post, 50, limit: 100
66
+ end
67
+
68
+ user = User.create!
69
+ post = Post.create! user: user
70
+ post = Post.create! user: user
71
+ post = Post.create! user: user
72
+
73
+ Scorecard::Point.where(
74
+ context: 'new_post',
75
+ amount: 50
76
+ ).count.should == 2
77
+ end
78
+
79
+ it "respects timeframe options" do
80
+ Scorecard.configure do |config|
81
+ config.rules.add_rule_for_points :new_post, 50, timeframe: :day
82
+ end
83
+
84
+ user = User.create!
85
+ post = Post.create! user: user
86
+ post = Post.create! user: user
87
+
88
+ Scorecard::Point.where(
89
+ context: 'new_post',
90
+ amount: 50
91
+ ).count.should == 1
92
+ end
93
+
94
+ it "allows for processing via Sidekiq" do
95
+ Scorecard.configure do |config|
96
+ config.rules.add_rule_for_points :new_user, 20
97
+ end
98
+
99
+ user = User.create!
100
+ Scorecard::Points.score_async :new_user, gameable: user, user: user
101
+
102
+ Scorecard::Point.where(
103
+ context: 'new_user',
104
+ amount: 20,
105
+ identifier: user.id.to_s,
106
+ user_id: user.id,
107
+ user_type: 'User',
108
+ gameable_id: user.id,
109
+ gameable_type: 'User'
110
+ ).should_not be_empty
111
+ end
112
+ end
@@ -0,0 +1,7 @@
1
+ class Post < ActiveRecord::Base
2
+ belongs_to :user
3
+
4
+ after_create lambda { |post|
5
+ Scorecard::Points.score :new_post, gameable: post
6
+ }
7
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ #
3
+ end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/combustion_test.sqlite
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -0,0 +1,13 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :posts, :force => true do |table|
3
+ table.string :subject
4
+ table.text :content
5
+ table.integer :user_id
6
+ table.timestamps
7
+ end
8
+
9
+ create_table :users, :force => true do |table|
10
+ table.string :email
11
+ table.timestamps
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,18 @@
1
+ require 'bundler'
2
+
3
+ Bundler.setup :default, :development
4
+
5
+ require 'combustion'
6
+ require 'sidekiq'
7
+ require 'sidekiq/testing/inline'
8
+ require 'scorecard'
9
+
10
+ Combustion.initialize! :active_record
11
+
12
+ require 'rspec/rails'
13
+
14
+ Dir["./spec/support/**/*.rb"].each { |file| require file }
15
+
16
+ RSpec.configure do |config|
17
+ config.use_transactional_fixtures = true
18
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scorecard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pat Allan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: combustion
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.5.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.14.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.14.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: sidekiq
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.15'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.15'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.8
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.8
83
+ description: Use an engine to track points, badges and levels in your Rails app.
84
+ email:
85
+ - pat@freelancing-gods.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .travis.yml
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - app/models/scorecard/point.rb
97
+ - config.ru
98
+ - db/migrate/1_create_points.rb
99
+ - lib/scorecard.rb
100
+ - lib/scorecard/engine.rb
101
+ - lib/scorecard/point_rule.rb
102
+ - lib/scorecard/points.rb
103
+ - lib/scorecard/rules.rb
104
+ - lib/scorecard/subscriber.rb
105
+ - lib/scorecard/worker.rb
106
+ - scorecard.gemspec
107
+ - spec/acceptance/points_spec.rb
108
+ - spec/internal/app/models/post.rb
109
+ - spec/internal/app/models/user.rb
110
+ - spec/internal/config/database.yml
111
+ - spec/internal/config/routes.rb
112
+ - spec/internal/db/schema.rb
113
+ - spec/internal/log/.gitignore
114
+ - spec/internal/public/favicon.ico
115
+ - spec/spec_helper.rb
116
+ homepage: https://github.com/inspire9/scorecard
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.1.2
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Rails Engine for common scorecard patterns
140
+ test_files:
141
+ - spec/acceptance/points_spec.rb
142
+ - spec/internal/app/models/post.rb
143
+ - spec/internal/app/models/user.rb
144
+ - spec/internal/config/database.yml
145
+ - spec/internal/config/routes.rb
146
+ - spec/internal/db/schema.rb
147
+ - spec/internal/log/.gitignore
148
+ - spec/internal/public/favicon.ico
149
+ - spec/spec_helper.rb