completeness-fu 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Rakefile +21 -0
- data/completeness-fu.gemspec +51 -0
- data/lib/completeness-fu.rb +29 -13
- data/lib/completeness-fu/{active_record_additions.rb → active_model_additions.rb} +43 -39
- data/readme.markdown +138 -0
- data/test/en.yml +1 -1
- data/test/helper.rb +19 -38
- data/test/scoring_test.rb +58 -65
- metadata +91 -20
- data/init.rb +0 -1
- data/test/debug.log +0 -1
- data/test/schema.rb +0 -6
- data/test/test.sqlite3 +0 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'although not required, its recommended you use bundler during development'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'rake'
|
9
|
+
|
10
|
+
Bundler::GemHelper.install_tasks
|
11
|
+
|
12
|
+
desc 'Default: run unit tests.'
|
13
|
+
task :default => [:test]
|
14
|
+
|
15
|
+
require 'rake/testtask'
|
16
|
+
Rake::TestTask.new(:test) do |t|
|
17
|
+
t.pattern = 'test/**/*_test.rb'
|
18
|
+
t.verbose = true
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/rdoctask'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{completeness-fu}
|
8
|
+
s.version = "0.6.0"
|
9
|
+
s.date = %q{2011-03-29}
|
10
|
+
|
11
|
+
s.summary = %q{Simple dsl for defining how to calculate how complete a model instance is (similar to LinkedIn profile completeness)}
|
12
|
+
s.description = s.summary
|
13
|
+
s.homepage = %q{http://github.com/joshk/completeness-fu}
|
14
|
+
|
15
|
+
s.authors = ["Josh Kalderimis"]
|
16
|
+
s.email = %q{josh.kalderimis@gmail.com}
|
17
|
+
|
18
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
19
|
+
|
20
|
+
s.extra_rdoc_files = ["readme.markdown"]
|
21
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
22
|
+
s.rubygems_version = %q{1.3.6}
|
23
|
+
|
24
|
+
s.require_paths = ['lib']
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
|
29
|
+
if s.respond_to? :specification_version then
|
30
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
31
|
+
s.specification_version = 3
|
32
|
+
|
33
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
34
|
+
s.add_development_dependency(%q<activemodel>, ["~> 3.0.0"])
|
35
|
+
s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
|
36
|
+
s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
|
37
|
+
s.add_development_dependency(%q<mocha>, ["~> 0.9.8"])
|
38
|
+
else
|
39
|
+
s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
|
40
|
+
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
41
|
+
s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
|
42
|
+
s.add_dependency(%q<mocha>, ["~> 0.9.8"])
|
43
|
+
end
|
44
|
+
else
|
45
|
+
s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
|
46
|
+
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
47
|
+
s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
|
48
|
+
s.add_dependency(%q<mocha>, ["~> 0.9.8"])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/completeness-fu.rb
CHANGED
@@ -1,20 +1,36 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module CompletenessFu
|
2
|
+
class CompletenessFuError < RuntimeError; end
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class << self
|
5
|
+
attr_accessor :common_weightings
|
6
|
+
attr_accessor :default_weighting
|
7
|
+
attr_accessor :default_i18n_namespace
|
8
|
+
attr_accessor :default_gradings
|
7
9
|
end
|
8
|
-
end
|
9
10
|
|
11
|
+
self.common_weightings = { :low => 20, :medium => 40, :high => 60 }
|
12
|
+
|
13
|
+
self.default_weighting = 40
|
10
14
|
|
11
|
-
|
15
|
+
self.default_i18n_namespace = [:completeness_scoring, :models]
|
12
16
|
|
13
|
-
|
17
|
+
self.default_gradings = {
|
18
|
+
:poor => 0..24,
|
19
|
+
:low => 25..49,
|
20
|
+
:medium => 50..79,
|
21
|
+
:high => 80..100
|
22
|
+
}
|
23
|
+
|
24
|
+
def self.setup_orm(orm_class)
|
25
|
+
orm_class.class_eval { include CompletenessFu::ActiveModelAdditions }
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'completeness-fu/active_model_additions'
|
31
|
+
require 'completeness-fu/scoring_builder'
|
14
32
|
|
15
|
-
CompletenessFu.default_i18n_namespace = [:completeness_scoring, :models]
|
16
33
|
|
17
|
-
CompletenessFu.
|
18
|
-
|
19
|
-
|
20
|
-
:high => 80..100 }
|
34
|
+
CompletenessFu.setup_orm(::ActiveRecord::Base) if defined?(::ActiveRecord::Base)
|
35
|
+
CompletenessFu.setup_orm(::CassandraObject::Base) if defined?(::CassandraObject::Base)
|
36
|
+
CompletenessFu.setup_orm(::Mongoid::Document) if defined?(::Mongoid::Document)
|
@@ -1,86 +1,90 @@
|
|
1
|
+
require 'active_support/core_ext/class/inheritable_attributes'
|
2
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
3
|
+
|
1
4
|
module CompletenessFu
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
attr_accessor :default_weighting
|
6
|
-
attr_accessor :default_i18n_namespace
|
7
|
-
attr_accessor :default_gradings
|
8
|
-
end
|
9
|
-
|
10
|
-
class CompletenessFuError < Exception; end
|
11
|
-
|
12
|
-
module ActiveRecordAdditions
|
13
|
-
|
5
|
+
|
6
|
+
module ActiveModelAdditions
|
7
|
+
|
14
8
|
def self.included(base)
|
15
9
|
base.class_eval do
|
16
10
|
def self.define_completeness_scoring(&checks_block)
|
11
|
+
unless self.include?(ActiveModel::Naming)
|
12
|
+
raise CompletenessFuError, 'please make sure ActiveModel::Naming is included so completeness_scoring can translate messages correctly'
|
13
|
+
end
|
14
|
+
|
17
15
|
class_inheritable_array :completeness_checks
|
18
16
|
cattr_accessor :default_weighting
|
19
17
|
cattr_accessor :model_weightings
|
20
|
-
|
18
|
+
|
21
19
|
self.send :extend, ClassMethods
|
22
20
|
self.send :include, InstanceMethods
|
23
|
-
|
21
|
+
|
24
22
|
checks_results = CompletenessFu::ScoringBuilder.generate(self, &checks_block)
|
25
|
-
|
23
|
+
|
26
24
|
self.default_weighting = checks_results[:default_weighting]
|
27
25
|
self.completeness_checks = checks_results[:completeness_checks]
|
28
26
|
self.model_weightings = checks_results[:model_weightings]
|
29
|
-
|
27
|
+
|
28
|
+
if checks_results[:cache_score_details]
|
29
|
+
unless self.include?(ActiveModel::Validations::Callbacks)
|
30
|
+
raise CompletenessFuError, 'please make sure ActiveModel::Validations::Callbacks is included before define_completeness_scoring if you want to cache competeness scoring'
|
31
|
+
end
|
32
|
+
self.before_validation checks_results[:cache_score_details]
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
33
|
-
|
34
|
-
|
37
|
+
|
38
|
+
|
35
39
|
module ClassMethods
|
36
40
|
def max_completeness_score
|
37
41
|
self.completeness_checks.inject(0) { |score, check| score += check[:weighting] }
|
38
42
|
end
|
39
43
|
end
|
40
|
-
|
41
|
-
|
44
|
+
|
45
|
+
|
42
46
|
module InstanceMethods
|
43
47
|
# returns an array of hashes with the translated name, description + weighting
|
44
48
|
def failed_checks
|
45
49
|
all_checks_which_pass(false)
|
46
50
|
end
|
47
|
-
|
51
|
+
|
48
52
|
# returns an array of hashes with the translated name, description + weighting
|
49
53
|
def passed_checks
|
50
54
|
all_checks_which_pass
|
51
55
|
end
|
52
|
-
|
56
|
+
|
53
57
|
# returns the absolute complete score
|
54
58
|
def completeness_score
|
55
59
|
sum_score = 0
|
56
60
|
passed_checks.each { |check| sum_score += check[:weighting] }
|
57
61
|
sum_score
|
58
62
|
end
|
59
|
-
|
63
|
+
|
60
64
|
# returns the percentage of completeness (relative score)
|
61
65
|
def percent_complete
|
62
66
|
self.completeness_score.to_f / self.class.max_completeness_score.to_f * 100
|
63
67
|
end
|
64
|
-
|
68
|
+
|
65
69
|
# returns a basic 'grading' based on percent_complete, defaults are :high, :medium, :low, and :poor
|
66
70
|
def completeness_grade
|
67
|
-
CompletenessFu.default_gradings.each do |grading|
|
68
|
-
return grading.first if grading.last.include?(self.percent_complete.round)
|
71
|
+
CompletenessFu.default_gradings.each do |grading|
|
72
|
+
return grading.first if grading.last.include?(self.percent_complete.round)
|
69
73
|
end
|
70
74
|
raise CompletenessFuError, "grade could not be determined with percent complete #{self.percent_complete.round}"
|
71
75
|
end
|
72
|
-
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
+
|
77
|
+
|
78
|
+
private
|
79
|
+
|
76
80
|
def all_checks_which_pass(should_pass = true)
|
77
81
|
self.completeness_checks.inject([]) do |results, check|
|
78
|
-
check_result = run_check(check[:check])
|
82
|
+
check_result = run_check(check[:check])
|
79
83
|
results << translate_check_details(check) if (should_pass ? check_result : !check_result)
|
80
84
|
results
|
81
85
|
end
|
82
86
|
end
|
83
|
-
|
87
|
+
|
84
88
|
def run_check(check)
|
85
89
|
case check
|
86
90
|
when Proc
|
@@ -91,18 +95,18 @@ module CompletenessFu
|
|
91
95
|
raise CompletenessFuError, "check of type #{check.class} not acceptable"
|
92
96
|
end
|
93
97
|
end
|
94
|
-
|
98
|
+
|
95
99
|
def translate_check_details(full_check)
|
96
|
-
namespace = CompletenessFu.default_i18n_namespace + [self.class.
|
97
|
-
|
100
|
+
namespace = CompletenessFu.default_i18n_namespace + [self.class.model_name.underscore.to_sym, full_check[:name]]
|
101
|
+
|
98
102
|
translations = [:title, :description, :extra].inject({}) do |list, field|
|
99
103
|
list[field] = I18n.t(field.to_sym, :scope => namespace)
|
100
104
|
list
|
101
105
|
end
|
102
|
-
|
106
|
+
|
103
107
|
full_check.merge(translations)
|
104
108
|
end
|
105
|
-
|
109
|
+
|
106
110
|
def cache_completeness_score(score_type)
|
107
111
|
score = case score_type
|
108
112
|
when :relative
|
@@ -116,7 +120,7 @@ module CompletenessFu
|
|
116
120
|
true
|
117
121
|
end
|
118
122
|
end
|
119
|
-
|
123
|
+
|
120
124
|
end
|
121
|
-
|
125
|
+
|
122
126
|
end
|
data/readme.markdown
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
Welcome to Completeness-Fu
|
2
|
+
==========================
|
3
|
+
|
4
|
+
In short, completeness-fu for ActiveModel allows you cleanly define the way a model instance is scored for completeness, similar to LinkedIn user profiles.
|
5
|
+
|
6
|
+
|
7
|
+
When should I use it?
|
8
|
+
---------------------
|
9
|
+
|
10
|
+
When you want your objects/models/instances to be of a certain standard, if it be data presence, format or what values are allowed and not allowed,
|
11
|
+
before an object can be saved or updated then you use validations. But when you want to allow more information to be entered, thus relaxing some
|
12
|
+
of the validation rules and prompt the user to enrich the data, then completeness-fu is your cup of tea.
|
13
|
+
|
14
|
+
Take an events web site for example, you may want to import information from various sources which might have varied data quality.
|
15
|
+
If your validations are too strict then the information won't import, but if you relax your validations too much then you risk
|
16
|
+
having quantity but not quality. What you can do is relax the validations and add some quality checks which calculate a score
|
17
|
+
which is then used to determine if the event information is shown on the public site or is listed in the admin panel prompting
|
18
|
+
staff to enrich the data before it is made public.
|
19
|
+
|
20
|
+
|
21
|
+
What does it work with?
|
22
|
+
-----------------------
|
23
|
+
|
24
|
+
Completeness-Fu used to be for ActiveRecord only, but this has changed with the latest version with only ActiveModel needed as a gem dependency (and a little ActiveSupport). This means that the latest version works with the following ORMs :
|
25
|
+
|
26
|
+
- ActiveRecord
|
27
|
+
|
28
|
+
- Mongoid
|
29
|
+
|
30
|
+
- Cassandra Object
|
31
|
+
|
32
|
+
The `define_completeness_scoring` method is mixed into the above mentioned ORMs by default, but if you want to use Completeness-Fu in an ActiveModel model which isn't one of the above ORMs, make sure you include `ActiveModel::Naming`. If you want to cache the completeness score make sure you also include `ActiveModel::Validations` and `ActiveModel::Validations::Callbacks`.
|
33
|
+
|
34
|
+
|
35
|
+
How do I use it?
|
36
|
+
----------------
|
37
|
+
|
38
|
+
If you want your model to only be regarded as complete if it has a title, description, and picture, you can do the following:
|
39
|
+
|
40
|
+
define_completeness_scoring do
|
41
|
+
check :title, lambda { |per| per.title.present? }, :high
|
42
|
+
check :description, lambda { |per| per.description.present? }, :medium
|
43
|
+
check :main_image, lambda { |per| per.main_image? }, :low
|
44
|
+
end
|
45
|
+
|
46
|
+
You also get the following methods for free on the model instance : _passed\_checks_, _failed\_checks_, _completeness\_score_, _percent\_complete_.
|
47
|
+
|
48
|
+
You can add the following to an initializer to set some defaults:
|
49
|
+
|
50
|
+
CompletenessFu.common_weights = { :low => 30, :medium => 50, :high => 70 }
|
51
|
+
|
52
|
+
CompletenessFu.default_weighting = :medium
|
53
|
+
|
54
|
+
`weights` and `default_weighting` can be changed per model, and you can use symbols for the checks instead of lambdas, thus allowing you to place check logic as methods in your class (public or private).
|
55
|
+
|
56
|
+
define_completeness_scoring do
|
57
|
+
weights :low => 30, :super_high => 60
|
58
|
+
default_weighting :medium
|
59
|
+
|
60
|
+
check :title, lambda { |per| per.title.present? }, :super_high
|
61
|
+
check :description, :description_present?
|
62
|
+
check :main_image, :main_image_or_pretty_picture?, :low
|
63
|
+
end
|
64
|
+
|
65
|
+
And if you want to cache the score to a field so you can use it in database searches you just have to add the following:
|
66
|
+
|
67
|
+
define_completeness_scoring do
|
68
|
+
cache_score :absolute # the default is :relative
|
69
|
+
|
70
|
+
check :title, lambda { |per| per.title.present? }, :super_high
|
71
|
+
check :description, :description_present?
|
72
|
+
end
|
73
|
+
|
74
|
+
If you are not using one of the above ORMs, make sure `ActiveModel::Validations` and `ActiveModel::Validations::Callbacks` are included.
|
75
|
+
|
76
|
+
|
77
|
+
Passed checks, Failed checks and i18n
|
78
|
+
----------------------------------------
|
79
|
+
|
80
|
+
Both the _passed\_checks_ and _failed\_checks_ methods return an array of hashes with extended information about the check.
|
81
|
+
The check hash includes a translated title, description and extra information which is based on the name of the check.
|
82
|
+
The translation structure is as such:
|
83
|
+
|
84
|
+
en:
|
85
|
+
completeness_scoring:
|
86
|
+
models:
|
87
|
+
my_model_name:
|
88
|
+
name_of_check:
|
89
|
+
title: 'Title'
|
90
|
+
description: 'The Check Description'
|
91
|
+
extra: 'Extra Info'
|
92
|
+
|
93
|
+
|
94
|
+
Up and coming features
|
95
|
+
----------------------
|
96
|
+
|
97
|
+
- enhance caching so filter type can be changed and field to save score to can be customized
|
98
|
+
- ability to 'share' common lambdas
|
99
|
+
- better docs
|
100
|
+
- increase test coverage (error edge cases, ORM inclusion)
|
101
|
+
|
102
|
+
|
103
|
+
Change Log
|
104
|
+
----------
|
105
|
+
|
106
|
+
4 Sep 10
|
107
|
+
|
108
|
+
- added bundler for development and testing
|
109
|
+
- make the plugin ORM agnostic, only ActiveModel is required
|
110
|
+
|
111
|
+
15 Oct 09
|
112
|
+
|
113
|
+
- bug fix for failed checks when check is a symbol to a method
|
114
|
+
- bug fix for grading (percent complete needed to be rounded)
|
115
|
+
- change of CompletenessFu.default\_weightings to CompletenessFu.default\_weighting
|
116
|
+
- change of CompletenessFu.default\_grading to CompletenessFu.default\_gradings
|
117
|
+
|
118
|
+
29 Sep 09
|
119
|
+
|
120
|
+
- added a 'grading' method which returns either :poor, :low, :medium or :high based on its percentage complete
|
121
|
+
- added ability to customize the default translations namespace using CompletenessFu.default\_i18n\_namespace
|
122
|
+
|
123
|
+
28 Sep 09
|
124
|
+
|
125
|
+
- move the scoring check builder into its own class so that it works within a clean room
|
126
|
+
|
127
|
+
24 Sep 09
|
128
|
+
|
129
|
+
- options to save the score to a field (caching) - good for searching on
|
130
|
+
- define methods on the class to use in the checks
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
Contributors
|
135
|
+
------------
|
136
|
+
|
137
|
+
- Peter (pero-ict)
|
138
|
+
- Andrew Brown (omenking)
|
data/test/en.yml
CHANGED
data/test/helper.rb
CHANGED
@@ -1,56 +1,37 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'test/unit'
|
3
|
-
require 'shoulda'
|
4
|
-
require 'mocha'
|
5
|
-
|
6
|
-
require 'active_record'
|
7
|
-
require 'action_controller'
|
8
|
-
|
9
1
|
begin
|
10
|
-
require '
|
2
|
+
require 'bundler/setup'
|
11
3
|
rescue LoadError
|
12
|
-
puts
|
4
|
+
puts 'although not required, its recommended you use bundler when running the tests'
|
13
5
|
end
|
14
6
|
|
15
|
-
|
7
|
+
require 'test/unit'
|
8
|
+
require 'shoulda'
|
9
|
+
require 'mocha'
|
16
10
|
|
17
|
-
|
18
|
-
$LOAD_PATH << File.join(ROOT, 'lib', 'completeness-fu')
|
11
|
+
require 'active_model'
|
19
12
|
|
20
|
-
require
|
13
|
+
require 'completeness-fu'
|
21
14
|
|
22
15
|
|
23
|
-
|
16
|
+
ROOT = File.join(File.dirname(__FILE__), '..')
|
24
17
|
|
25
|
-
File.
|
26
|
-
ActiveRecord::Base.establish_connection(
|
27
|
-
"adapter" => "sqlite3", "database" => TEST_DATABASE_FILE
|
28
|
-
)
|
18
|
+
I18n.load_path << File.join(ROOT, 'test', 'en.yml')
|
29
19
|
|
30
|
-
RAILS_DEFAULT_LOGGER = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
|
31
20
|
|
32
|
-
|
21
|
+
def rebuild_class(class_name)
|
22
|
+
Object.send(:remove_const, class_name) rescue nil
|
33
23
|
|
24
|
+
klass = Object.const_set(class_name, Class.new)
|
34
25
|
|
35
|
-
|
26
|
+
klass.class_eval do
|
27
|
+
include ActiveModel::Naming
|
28
|
+
include ActiveModel::Validations
|
29
|
+
include ActiveModel::Validations::Callbacks
|
36
30
|
|
31
|
+
include CompletenessFu::ActiveModelAdditions
|
37
32
|
|
38
|
-
|
39
|
-
ActiveRecord::Base.send(:include, CompletenessFu::ActiveRecordAdditions)
|
40
|
-
Object.send(:remove_const, "ScoringTest") rescue nil
|
41
|
-
Object.const_set("ScoringTest", Class.new(ActiveRecord::Base))
|
42
|
-
ScoringTest.class_eval do
|
43
|
-
include CompletenessFu::ActiveRecordAdditions
|
44
|
-
define_completeness_scoring do
|
45
|
-
check :title, lambda { |test| test.title.present? }, 20
|
46
|
-
end
|
33
|
+
attr_accessor :title
|
47
34
|
end
|
48
|
-
end
|
49
35
|
|
50
|
-
def reset_class class_name
|
51
|
-
ActiveRecord::Base.send(:include, CompletenessFu::ActiveRecordAdditions)
|
52
|
-
Object.send(:remove_const, class_name) rescue nil
|
53
|
-
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
|
54
|
-
klass.class_eval{ include CompletenessFu::ActiveRecordAdditions }
|
55
36
|
klass
|
56
|
-
end
|
37
|
+
end
|
data/test/scoring_test.rb
CHANGED
@@ -1,153 +1,146 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'test/helper'
|
3
3
|
|
4
|
-
|
5
4
|
class ScoringTest < Test::Unit::TestCase
|
6
|
-
|
7
|
-
context "
|
5
|
+
|
6
|
+
context "A class which includes ActiveModel" do
|
7
|
+
setup { rebuild_class('ScoringTest') }
|
8
|
+
|
8
9
|
should "have a define_completeness_scoring mixed in" do
|
9
|
-
reset_class 'ScoringTest'
|
10
10
|
assert ScoringTest.methods.include?('define_completeness_scoring')
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
14
|
-
|
13
|
+
|
15
14
|
context "A class with scoring defined" do
|
16
15
|
setup do
|
17
|
-
|
18
|
-
|
16
|
+
klass = rebuild_class('ScoringTest')
|
17
|
+
klass.class_eval do
|
19
18
|
define_completeness_scoring do
|
20
19
|
check :title, lambda { |test| test.title.present? }, 20
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
24
|
-
|
23
|
+
|
25
24
|
should "have one scoring check" do
|
26
25
|
assert_equal 1, ScoringTest.completeness_checks.size
|
27
26
|
end
|
28
|
-
|
27
|
+
|
29
28
|
should "have one failed check" do
|
30
29
|
st = ScoringTest.new
|
31
30
|
assert_equal 1, st.failed_checks.size
|
32
31
|
end
|
33
|
-
|
34
|
-
|
32
|
+
|
35
33
|
context "and with one complete check" do
|
36
34
|
setup do
|
37
35
|
@st = ScoringTest.new
|
38
36
|
@st.title = 'I have a title'
|
39
37
|
end
|
40
|
-
|
38
|
+
|
41
39
|
should "have an absolute completeness score of 20" do
|
42
40
|
assert_equal 20, @st.completeness_score
|
43
41
|
end
|
44
|
-
|
42
|
+
|
45
43
|
should "have a relative completeness score of 0 (percent complete)" do
|
46
44
|
assert_equal 100, @st.percent_complete
|
47
45
|
end
|
48
|
-
|
46
|
+
|
49
47
|
should "have a description" do
|
50
48
|
assert_equal "The Scoring Test Description", @st.passed_checks.first[:description]
|
51
49
|
end
|
52
50
|
end
|
53
51
|
end
|
54
|
-
|
55
|
-
|
52
|
+
|
56
53
|
context "A class with scoring defined with no weighting" do
|
57
54
|
setup do
|
58
|
-
|
59
|
-
|
55
|
+
klass = rebuild_class('ScoringTest')
|
56
|
+
klass.class_eval do
|
60
57
|
define_completeness_scoring do
|
61
58
|
check :title, lambda { |test| test.title.present? }
|
62
59
|
end
|
63
60
|
end
|
64
|
-
@st =
|
61
|
+
@st = klass.new
|
65
62
|
@st.title = 'I have a title'
|
66
63
|
end
|
67
|
-
|
64
|
+
|
68
65
|
should "have the default scoring used" do
|
69
66
|
assert_equal 40, @st.completeness_score
|
70
67
|
end
|
71
68
|
end
|
72
69
|
|
73
|
-
|
74
70
|
context "A class with scoring defined with a symbol weighting" do
|
75
71
|
setup do
|
76
|
-
|
77
|
-
|
72
|
+
klass = rebuild_class('ScoringTest')
|
73
|
+
klass.class_eval do
|
78
74
|
define_completeness_scoring do
|
79
75
|
check :title, lambda { |test| test.title.present? }, :high
|
80
76
|
end
|
81
77
|
end
|
82
|
-
@st =
|
78
|
+
@st = klass.new
|
83
79
|
@st.title = 'I have a title'
|
84
80
|
end
|
85
|
-
|
81
|
+
|
86
82
|
should "have a scoring from the common_weightings hash used used" do
|
87
83
|
assert_equal ScoringTest.model_weightings[:high], @st.completeness_score
|
88
84
|
end
|
89
85
|
end
|
90
|
-
|
91
|
-
|
86
|
+
|
92
87
|
context "A class with scoring defined with a custom weighting" do
|
93
88
|
setup do
|
94
|
-
|
95
|
-
|
89
|
+
klass = rebuild_class('ScoringTest')
|
90
|
+
klass.class_eval do
|
96
91
|
define_completeness_scoring do
|
97
92
|
weightings :super_high => 80
|
98
93
|
check :title, lambda { |test| test.title.present? }, :super_high
|
99
94
|
end
|
100
95
|
end
|
101
|
-
@st =
|
96
|
+
@st = klass.new
|
102
97
|
@st.title = 'I have a title'
|
103
98
|
end
|
104
|
-
|
99
|
+
|
105
100
|
should "have a scoring from the common_weightings hash used used" do
|
106
101
|
assert_equal ScoringTest.model_weightings[:super_high], @st.completeness_score
|
107
102
|
end
|
108
103
|
end
|
109
|
-
|
110
|
-
|
104
|
+
|
111
105
|
context "A class with scoring defined with a custom weighting and no common weightings" do
|
112
106
|
setup do
|
113
|
-
|
114
|
-
|
107
|
+
klass = rebuild_class('ScoringTest')
|
108
|
+
klass.class_eval do
|
115
109
|
define_completeness_scoring do
|
116
110
|
weightings :super_high => 80 , :merge_with_common => false
|
117
111
|
check :title, lambda { |test| test.title.present? }, :super_high
|
118
112
|
end
|
119
113
|
end
|
120
|
-
@st =
|
114
|
+
@st = klass.new
|
121
115
|
@st.title = 'I have a title'
|
122
116
|
end
|
123
|
-
|
117
|
+
|
124
118
|
should "have a scoring from the common_weightings hash used used" do
|
125
119
|
assert_equal nil, ScoringTest.model_weightings[:high]
|
126
120
|
assert_equal ScoringTest.model_weightings[:super_high], @st.completeness_score
|
127
121
|
end
|
128
122
|
end
|
129
|
-
|
130
|
-
|
123
|
+
|
131
124
|
context "A class with scoring defined with a check using a symbol to a private method" do
|
132
125
|
setup do
|
133
|
-
|
134
|
-
|
126
|
+
klass = rebuild_class('ScoringTest')
|
127
|
+
klass.class_eval do
|
135
128
|
define_completeness_scoring do
|
136
129
|
check :title, :title_present?, :high
|
137
130
|
check :name, :name_present?, :high
|
138
131
|
end
|
139
132
|
|
140
133
|
private
|
141
|
-
def title_present?
|
142
|
-
self.title.present?
|
143
|
-
end
|
144
134
|
|
145
|
-
|
146
|
-
|
147
|
-
|
135
|
+
def title_present?
|
136
|
+
self.title.present?
|
137
|
+
end
|
148
138
|
|
139
|
+
def name_present?
|
140
|
+
false
|
141
|
+
end
|
149
142
|
end
|
150
|
-
@st =
|
143
|
+
@st = klass.new
|
151
144
|
@st.title = 'I have a title'
|
152
145
|
end
|
153
146
|
|
@@ -164,45 +157,45 @@ class ScoringTest < Test::Unit::TestCase
|
|
164
157
|
end
|
165
158
|
|
166
159
|
end
|
167
|
-
|
168
|
-
|
160
|
+
|
169
161
|
context "A class with scoring defined and cache to field directive" do
|
170
162
|
setup do
|
171
|
-
|
172
|
-
|
163
|
+
klass = rebuild_class('ScoringTest')
|
164
|
+
klass.class_eval do
|
173
165
|
define_completeness_scoring do
|
174
166
|
cache_score :absolute
|
175
167
|
check :title, :title_present?, :high
|
176
168
|
end
|
177
|
-
|
169
|
+
|
170
|
+
attr_accessor :cached_completeness_score
|
171
|
+
|
178
172
|
private
|
179
|
-
|
180
|
-
|
181
|
-
|
173
|
+
def title_present?
|
174
|
+
self.title.present?
|
175
|
+
end
|
182
176
|
end
|
183
|
-
@st =
|
177
|
+
@st = klass.new
|
184
178
|
@st.title = 'I have a title'
|
185
179
|
end
|
186
|
-
|
180
|
+
|
187
181
|
should "have a before filter added, and save the defined calculation to the default field" do
|
188
182
|
assert_equal nil, @st.cached_completeness_score
|
189
183
|
@st.valid?
|
190
184
|
assert_equal @st.completeness_score, @st.cached_completeness_score
|
191
185
|
end
|
192
186
|
end
|
193
|
-
|
194
|
-
|
187
|
+
|
195
188
|
context "A class with scoring" do
|
196
189
|
setup do
|
197
|
-
|
198
|
-
|
190
|
+
klass = rebuild_class('ScoringTest')
|
191
|
+
klass.class_eval do
|
199
192
|
define_completeness_scoring do
|
200
193
|
check :title, lambda { |test| test.title.present? }, :high
|
201
194
|
end
|
202
195
|
end
|
203
|
-
@st =
|
196
|
+
@st = klass.new
|
204
197
|
end
|
205
|
-
|
198
|
+
|
206
199
|
should "have a grade of :high" do
|
207
200
|
assert_equal :poor, @st.completeness_grade
|
208
201
|
@st.title = 'I have a title'
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: completeness-fu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 7
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Josh Kalderimis
|
@@ -9,33 +15,95 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2011-03-29 00:00:00 +02:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
|
-
|
17
|
-
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
20
24
|
requirements:
|
21
|
-
- -
|
25
|
+
- - ~>
|
22
26
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
25
|
-
|
27
|
+
hash: 7
|
28
|
+
segments:
|
29
|
+
- 3
|
30
|
+
- 0
|
31
|
+
- 0
|
32
|
+
version: 3.0.0
|
33
|
+
prerelease: false
|
34
|
+
type: :development
|
35
|
+
requirement: *id001
|
36
|
+
name: activemodel
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 49
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
- 8
|
47
|
+
- 7
|
48
|
+
version: 0.8.7
|
49
|
+
prerelease: false
|
50
|
+
type: :development
|
51
|
+
requirement: *id002
|
52
|
+
name: rake
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 37
|
60
|
+
segments:
|
61
|
+
- 2
|
62
|
+
- 11
|
63
|
+
- 3
|
64
|
+
version: 2.11.3
|
65
|
+
prerelease: false
|
66
|
+
type: :development
|
67
|
+
requirement: *id003
|
68
|
+
name: shoulda
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 43
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
- 9
|
79
|
+
- 8
|
80
|
+
version: 0.9.8
|
81
|
+
prerelease: false
|
82
|
+
type: :development
|
83
|
+
requirement: *id004
|
84
|
+
name: mocha
|
85
|
+
description: Simple dsl for defining how to calculate how complete a model instance is (similar to LinkedIn profile completeness)
|
26
86
|
email: josh.kalderimis@gmail.com
|
27
87
|
executables: []
|
28
88
|
|
29
89
|
extensions: []
|
30
90
|
|
31
|
-
extra_rdoc_files:
|
32
|
-
|
91
|
+
extra_rdoc_files:
|
92
|
+
- readme.markdown
|
33
93
|
files:
|
94
|
+
- .gitignore
|
95
|
+
- Gemfile
|
96
|
+
- Gemfile.lock
|
97
|
+
- Rakefile
|
34
98
|
- VERSION.yml
|
35
|
-
-
|
99
|
+
- completeness-fu.gemspec
|
36
100
|
- lib/completeness-fu.rb
|
37
|
-
- lib/completeness-fu/
|
101
|
+
- lib/completeness-fu/active_model_additions.rb
|
38
102
|
- lib/completeness-fu/scoring_builder.rb
|
103
|
+
- readme.markdown
|
104
|
+
- test/en.yml
|
105
|
+
- test/helper.rb
|
106
|
+
- test/scoring_test.rb
|
39
107
|
has_rdoc: true
|
40
108
|
homepage: http://github.com/joshk/completeness-fu
|
41
109
|
licenses: []
|
@@ -46,28 +114,31 @@ rdoc_options:
|
|
46
114
|
require_paths:
|
47
115
|
- lib
|
48
116
|
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
49
118
|
requirements:
|
50
119
|
- - ">="
|
51
120
|
- !ruby/object:Gem::Version
|
121
|
+
hash: 3
|
122
|
+
segments:
|
123
|
+
- 0
|
52
124
|
version: "0"
|
53
|
-
version:
|
54
125
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
55
127
|
requirements:
|
56
128
|
- - ">="
|
57
129
|
- !ruby/object:Gem::Version
|
130
|
+
hash: 3
|
131
|
+
segments:
|
132
|
+
- 0
|
58
133
|
version: "0"
|
59
|
-
version:
|
60
134
|
requirements: []
|
61
135
|
|
62
136
|
rubyforge_project:
|
63
|
-
rubygems_version: 1.
|
137
|
+
rubygems_version: 1.5.0
|
64
138
|
signing_key:
|
65
139
|
specification_version: 3
|
66
140
|
summary: Simple dsl for defining how to calculate how complete a model instance is (similar to LinkedIn profile completeness)
|
67
141
|
test_files:
|
68
|
-
- test/debug.log
|
69
142
|
- test/en.yml
|
70
143
|
- test/helper.rb
|
71
|
-
- test/schema.rb
|
72
144
|
- test/scoring_test.rb
|
73
|
-
- test/test.sqlite3
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'completeness-fu'
|
data/test/debug.log
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# Logfile created on Wed Sep 23 14:40:19 +0200 2009 by logger.rb/22285
|
data/test/schema.rb
DELETED
data/test/test.sqlite3
DELETED
Binary file
|