zitdunyet 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zitdunyet.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 David Pellegrini
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,139 @@
1
+ # Zitdunyet
2
+
3
+ We're all familiar with social websites where you create an account and then are encouraged to provide information about yourself. They often have something when you login that announces "Your profile is 55% complete. Add a photo to reach 70%!" The notion of completeness can be applied to objects of all sort, to processes, and so on.
4
+
5
+ This gem provides a general solution for expressing and assessing completeness. It includes:
6
+
7
+ + a DSL for expressing the conditions
8
+ + methods to report completeness as True/False or percentage
9
+ + a method to provide hints for completion
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'zitdunyet'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install zitdunyet
24
+
25
+ ## Usage
26
+
27
+ To use the gem, first require the gem and include the Completeness module into the class for which you want to track completeness.
28
+
29
+ require 'zitdunyet'
30
+
31
+ class UserAccount
32
+ include Zitdunyet::Completeness
33
+
34
+ attr_accessor :name, :birthday, :favorite_color, :friends
35
+ ... etc ...
36
+ end
37
+
38
+ If you'd rather separate concerns, create a Decorator for your model and include the Completeness module there.
39
+
40
+ require 'zitdunyet'
41
+
42
+ class UserAccountCompleteness
43
+ include Zitdunyet::Completeness
44
+
45
+ def initialize(user_account)
46
+ @user_accout = user_account
47
+ end
48
+ ... etc ...
49
+ end
50
+
51
+ ### Basic use case
52
+
53
+ I'm reminded of Larry Wall's admonition to make simple things easy and difficult things possible. In that spirit, let's start with a simple use case.
54
+
55
+ #### Checkoff items
56
+
57
+ The conditions for completeness are represented as check-off items. Check-off items are expressed using the #checkoff class macro.
58
+
59
+ The arguments are:
60
+
61
+ + label - a String or something that evaluates to a String to identify the item
62
+ + amount - how much of the completeness is tied to this item, expressed in percent or units
63
+ + options - hint to encourage completion of this item
64
+ + logic - a block to evaluate that determines if this item is done. The block is passed _self_ and evaluates to _true_/_false_.
65
+
66
+ Let's start with a simple use case in the context of UserAccount.
67
+
68
+ checkoff "Name", 30.percent do |s| s.name end
69
+ checkoff "Birthday", 25.percent do |s| s.birthday end
70
+ checkoff "Favorite Color", 15.percent do |s| not s.favorite_color.nil? end
71
+ checkoff "Friends", 30.percent do |s| s.friends.size >= 3 end
72
+
73
+ This basically gives credit when name, birthday, and favorite_color have values, and when the user has specified 3 or more friends. Of course, the details of enforcing good values for these fields (e.g., birthdate is actually a Date, friends is an array) is not shown and is your responsibility, not this gem's.
74
+
75
+ When writing the checkoff, you may chose to use ()'s and {}'s or break the do ... end over multiple lines if that's more your style. I adopted the style above because it reads well as a DSL. Ruby purists may have another opinion. Any valid Ruby method syntax is fair game, though.
76
+
77
+ checkoff("Favorite Color", 15.percent) { |s| s.favorite_color }
78
+ checkoff "Friends", 30.percent do |s|
79
+ s.friends.size >= 3
80
+ end
81
+
82
+ #### Testing completeness
83
+
84
+ Instances of the class can be tested for completeness by using instance methods defined in Completeness:
85
+
86
+ + #complete? - returns true if all of the check-off items' logic blocks evaluate to true; false otherwise
87
+ + #percent_complete - returns a number 0-100 that represents the sum of percentages associated with check-off items that evaluate to true. If the percentages specified for all of the check-off items total 100, then the result is a strict sum of the completed check-off items. If the percentages specified total less than 100, then the returned value is scaled to make up the shortfall.
88
+
89
+ #### Hints
90
+
91
+ If your UserAccount happens to be incomplete, it would be nice to know what you need to do to complete it.
92
+
93
+ + #hints - returns a hash in which the keys are the labels or hints of the uncompleted steps and the values are the percentages
94
+
95
+ By using descriptive labels you can give a decent prompt to the user.
96
+
97
+ checkoff "Birthday filled in", 25.percent do |s| s.birthday end
98
+ checkoff "At least 3 friends", 30.percent do |s| s.friends.size >= 3 end
99
+
100
+ Sometimes, though, you may want to be more elaborate in your hinting. #checkoff accepts an optional argument, :hint, for such situations. The hint must be a string or a block that evaluates to a String.
101
+
102
+ checkoff "Favorite Color", 15.percent, hint: "What's your favorite color?" do |s| s.favorite_color end
103
+ checkoff "Friends", 30.percent, hint: lambda {|s| "Add #{3-s.friends.size} friends"} do |s| s.friends.size >= 3 end
104
+
105
+ The hint over-rides the label when reported by #hints.
106
+
107
+ ### Advanced Usage
108
+
109
+ Basic usage is fine for most situations, but sometimes you need a little something extra.
110
+
111
+ #### Units
112
+
113
+ Percentages are easy to understand, but have the annoying property of having to total 100. As noted above, the #percent_complete method is forgiving when your totals fall short, but that may be surprising when you start seeing "43.375% complete" reported when you know all your specified percentages are on the 5's and 10's. Also, checkoff will squawk at you if you attempt to specify a percentage that puts you over 100.
114
+
115
+ Instead of percent, the checkoff amount can be expressed in units. This gives you the freedom to specify relative values at whatever scale you like. #percent_complete will calculate the percentages for you.
116
+
117
+ checkoff "Step 1", 1.unit do |s| s.step_1_done? end
118
+ checkoff "Step 2", 1.unit do |s| s.step_2_done? end
119
+ checkoff "Step 3", 1.unit do |s| s.step_3_done? end
120
+
121
+ This strategy will allow you to add a "Step 4" down the road without having to rejigger percentage.
122
+
123
+ #### Mixing Percent and Units
124
+
125
+ You may mix both percent and units in a series of checkoffs if desired. Let's say you want the user name to always count for 30% regardless of anything else. You could assign it 30% and assign units to the other items. #percent_complete will honor the explicit percents and scale the units into whatever remains.
126
+
127
+ #### Inheritance
128
+
129
+ Let's say you have a special account that subclasses UserAccount. You want the special account to have additional steps toward completion. No problem. Simply specify the additional checkoff items in the subclass. When evaluating completion, the checkoff items in the UserAccount will be evaluated along with the checkoff items in the subclass.
130
+
131
+ In situations where subclassing is a possibility it is good practice to specify the checkoff amounts in units rather than percent. That way the superclass can be complete relative to itself when instantiated, while allowing the subclass to contribute its share when it is instantiated without blowing the 100% barrier.
132
+
133
+ ## Contributing
134
+
135
+ 1. Fork it
136
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
137
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
138
+ 4. Push to the branch (`git push origin my-new-feature`)
139
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ # -*- ruby -*-
3
+ require "bundler/gem_tasks"
4
+
5
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
6
+
7
+ # RSpec
8
+ require "rspec/core/rake_task"
9
+ RSpec::Core::RakeTask.new('spec')
10
+
11
+ task :default => :spec
@@ -0,0 +1,40 @@
1
+ module Zitdunyet
2
+ class Checkoff
3
+ attr_accessor :label, :percent, :units, :logic, :hint, :stale, :when
4
+
5
+ def initialize(label, progress_amount, *opts, &logic)
6
+ # Fill in the required fields
7
+ (label.is_a? String) ? self.label = label : (raise ArgumentError.new "Label must be a String")
8
+ assign_progress(progress_amount)
9
+ block_given? ? self.logic = logic : (raise ArgumentError.new "Missing the block for assessing completion")
10
+
11
+ # Fill in the optional fields if present
12
+ assign_options(opts) unless opts.empty?
13
+ end
14
+
15
+ def assign_options(opts)
16
+ options = opts.first
17
+ self.hint = options.delete(:hint)
18
+ #self.stale = options.delete(:stale)
19
+ #self.when = options.delete(:when)
20
+ raise ArgumentError.new "Unrecognized options: #{options.inspect}" unless options.empty?
21
+
22
+ #if self.stale or self.when
23
+ # raise ArgumentError.new "Specified 'stale' without 'when'" unless self.when
24
+ # raise ArgumentError.new "Specified 'when' without 'stale'" unless self.stale
25
+ #end
26
+ end
27
+
28
+ def assign_progress(progress_amount)
29
+ if progress_amount.is_a? Zitdunyet::Unit
30
+ self.units=progress_amount.amount
31
+ elsif progress_amount.is_a? Zitdunyet::Percent
32
+ self.percent=progress_amount.amount
33
+ elsif progress_amount.is_a? Numeric
34
+ self.percent=progress_amount
35
+ else
36
+ raise ArgumentError.new "Invalid type for progress amount: #{progress_amount.class}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ module Zitdunyet
2
+ module ClassSpecific
3
+
4
+ def checklist
5
+ checklist = self.superclass.respond_to?(:checklist) ? self.superclass.send(:checklist) : []
6
+ @checklist ? checklist + @checklist : checklist
7
+ end
8
+
9
+ def checklist_add(item)
10
+ @checklist = (@checklist || []) << item
11
+ end
12
+
13
+ def percentage
14
+ percentage = self.superclass.respond_to?(:percentage) ? self.superclass.send(:percentage) : 0
15
+ percentage + (@percentage || 0)
16
+ end
17
+
18
+ def percentage=(amount)
19
+ @percentage = amount
20
+ end
21
+
22
+ def percentage_add(amount)
23
+ @percentage = (@percentage || 0) + amount
24
+ end
25
+
26
+ def units
27
+ units = self.superclass.respond_to?(:units) ? self.superclass.send(:units) : 0
28
+ units + (@units || 0)
29
+ end
30
+
31
+ def units=(amount)
32
+ @units = amount
33
+ end
34
+
35
+ def units_add(amount)
36
+ @units = (@units || 0) + amount
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ module Zitdunyet
2
+ module Completeness
3
+
4
+ def self.included(base)
5
+ base.extend Zitdunyet::ClassSpecific
6
+ base.extend Zitdunyet::Expressions
7
+ end
8
+
9
+ include Zitdunyet::Evaluation
10
+
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ module Zitdunyet
2
+ module Evaluation
3
+
4
+ def complete?
5
+ self.class.checklist.each { |item| return false unless item.logic.call(self) }
6
+ true
7
+ end
8
+
9
+ def percent_complete
10
+ # Scale the percentages if they don't add up to 100. When units are part of the mix, scale the units to fit into
11
+ # the percentage slice leftover after totaling the percentages.
12
+ unit_percentage = 0
13
+ pct_percentage = 1
14
+ if self.class.percentage < 100
15
+ unit_percentage = ((100 - self.class.percentage) / self.class.units.to_f) if (self.class.units > 0)
16
+ pct_percentage = (100 / self.class.percentage.to_f) if (self.class.units == 0)
17
+ elsif self.class.percentage > 100
18
+ pct_percentage = (100 / self.class.percentage.to_f) if (self.class.units == 0)
19
+ end
20
+
21
+ completed_pct = 0
22
+ completed_units = 0
23
+ complete = true
24
+ @hints = {}
25
+ self.class.checklist.each do |item|
26
+ if item.logic.call(self)
27
+ if item.percent
28
+ completed_pct += item.percent
29
+ else
30
+ completed_units += item.units if item.units
31
+ end
32
+ else
33
+ complete = false
34
+ hint = item.hint || item.label
35
+ @hints[hint] = item.percent ? item.percent * pct_percentage : item.units * unit_percentage
36
+ end
37
+ end
38
+ complete ? 100 : (completed_pct * pct_percentage) + (completed_units * unit_percentage)
39
+ end
40
+
41
+ def hints
42
+ percent_complete unless @hints
43
+ hints = {}
44
+ @hints.each_pair do |hint, pct|
45
+ if hint.respond_to? :call
46
+ hints[hint.call(self)] = pct
47
+ else
48
+ hints[hint.to_s] = pct
49
+ end
50
+ end
51
+ hints
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,14 @@
1
+ module Zitdunyet
2
+ module Expressions
3
+
4
+ def checkoff(label, progress_amount, *opts, &logic)
5
+ # Create an object to represent the check-off item
6
+ choff = Checkoff.new(label, progress_amount, *opts, &logic)
7
+ checklist_add choff
8
+ percentage_add(choff.percent) if choff.percent
9
+ units_add(choff.units) if choff.units
10
+ raise RangeError.new "Percentage total (#{percentage}) exceeds 100%" if percentage > 100
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Zitdunyet
2
+
3
+ class Percent
4
+
5
+ attr_accessor :amount
6
+
7
+ def initialize(amount)
8
+ self.amount=amount
9
+ end
10
+
11
+ end
12
+ end
13
+
14
+ class Numeric
15
+ def percent
16
+ Zitdunyet::Percent.new(self)
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module Zitdunyet
2
+
3
+ class Unit
4
+
5
+ attr_accessor :amount
6
+
7
+ def initialize(amount)
8
+ self.amount=amount
9
+ end
10
+
11
+ end
12
+ end
13
+
14
+ class Numeric
15
+ def unit
16
+ Zitdunyet::Unit.new(self)
17
+ end
18
+
19
+ alias units unit
20
+ end
@@ -0,0 +1,3 @@
1
+ module Zitdunyet
2
+ VERSION = "0.0.1"
3
+ end
data/lib/zitdunyet.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "zitdunyet/version"
2
+ require "zitdunyet/percent"
3
+ require "zitdunyet/unit"
4
+ require "zitdunyet/checkoff"
5
+ require "zitdunyet/class_specific"
6
+ require "zitdunyet/expressions"
7
+ require "zitdunyet/evaluation"
8
+ require "zitdunyet/completeness"
9
+
10
+ module Zitdunyet
11
+ # Your code goes here...
12
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Initialization" do
4
+
5
+ context "Assigning the label" do
6
+ it "should accept a String" do
7
+ label = "The Label"
8
+ co = Zitdunyet::Checkoff.new(label, 0) { "nuthin'" }
9
+ co.label.should == label
10
+ end
11
+
12
+ it "should reject anything besides a String" do
13
+ label = 1
14
+ expect { Zitdunyet::Checkoff.new(label, 10) { "nuthin'" } }.to raise_error(ArgumentError)
15
+ end
16
+ end
17
+
18
+ context "Assigning progress amount" do
19
+ it "should accept Unit" do
20
+ amount = 10.units
21
+ co = Zitdunyet::Checkoff.new("The Label", amount) { "nuthin'" }
22
+ co.units.should == amount.amount
23
+ end
24
+
25
+ it "should accept a Percent" do
26
+ amount = 10.percent
27
+ co = Zitdunyet::Checkoff.new("The Label", amount) { "nuthin'" }
28
+ co.percent.should == amount.amount
29
+ end
30
+
31
+ it "should treat Numeric as percentage" do
32
+ amount = 10
33
+ co = Zitdunyet::Checkoff.new("The Label", amount) { "nuthin'" }
34
+ co.percent.should == amount
35
+ end
36
+
37
+ it "should reject invalid type" do
38
+ amount = "ten"
39
+ expect { Zitdunyet::Checkoff.new("The Label", amount) { "nuthin'" } }.to raise_error(ArgumentError)
40
+ end
41
+ end
42
+
43
+ context "Specifying the completion logic" do
44
+ it "should accept an anonymous block" do
45
+ text = "this is a test"
46
+ co = Zitdunyet::Checkoff.new("The Label", 10) { text }
47
+ co.logic.call.should == text
48
+ end
49
+
50
+ it "should raise an exception if the block is missing" do
51
+ expect { Zitdunyet::Checkoff.new("The Label", 10) }.to raise_error(ArgumentError)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,85 @@
1
+ require "spec_helper"
2
+
3
+ describe "Class Attr" do
4
+
5
+ before(:each) do
6
+ begin
7
+ Object.send(:remove_const, :Baz)
8
+ rescue
9
+ end
10
+ begin
11
+ Object.send(:remove_const, :Foo)
12
+ rescue
13
+ end
14
+ end
15
+
16
+ after(:each) do
17
+ puts "= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ="
18
+ end
19
+
20
+ let(:foo) do
21
+ class Foo
22
+ extend Zitdunyet::ClassSpecific
23
+
24
+ def add(item)
25
+ self.class.checklist_add item
26
+ self.class.units_add 1
27
+ end
28
+
29
+ def chklst
30
+ self.class.checklist
31
+ end
32
+
33
+ def units
34
+ self.class.units
35
+ end
36
+
37
+ def dump
38
+ puts "Foo#dump: checklist=#{self.class.checklist}, units=#{self.class.units}"
39
+ end
40
+ end
41
+
42
+ Foo.new
43
+ end
44
+
45
+ let(:baz) do
46
+ class Baz < Foo
47
+ def dump
48
+ puts "Baz#dump: checklist=#{self.class.checklist}, units=#{self.class.units}"
49
+ end
50
+ end
51
+
52
+ Baz.new
53
+ end
54
+
55
+ it "should initialize with empty array and zeros" do
56
+ foo.chklst.should be_empty
57
+ foo.chklst.should have(0).items
58
+ foo.units.should == 0
59
+ end
60
+
61
+ it "should define class-specific attrs" do
62
+ foo.dump
63
+ foo.add("Hello")
64
+ foo.dump
65
+ foo.chklst.should have(1).items
66
+ foo.units.should == 1
67
+ end
68
+
69
+ it "should support inheritance" do
70
+ foo.dump
71
+ foo.add("Hello")
72
+ foo.dump
73
+ foo.chklst.should have(1).items
74
+
75
+ baz.dump
76
+ baz.add("World")
77
+ baz.dump
78
+ baz.chklst.should have(2).items
79
+ baz.units.should == 2
80
+
81
+ foo.dump
82
+ foo.chklst.should have(1).items
83
+ foo.units.should == 1
84
+ end
85
+ end
@@ -0,0 +1,282 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Evaluations" do
4
+
5
+ before(:each) do
6
+ begin
7
+ Object.send(:remove_const, :Baz)
8
+ rescue
9
+ end
10
+ begin
11
+ Object.send(:remove_const, :Foo)
12
+ rescue
13
+ end
14
+ end
15
+
16
+ context "basics" do
17
+ it "should evaluate true" do
18
+ class Foo
19
+ include Zitdunyet::Completeness
20
+ checkoff "Step One", 8.percent do true end
21
+ end
22
+
23
+ foo = Foo.new
24
+ foo.complete?.should be_true
25
+ end
26
+
27
+ it "should evaluate false" do
28
+ class Foo
29
+ include Zitdunyet::Completeness
30
+ checkoff "Step One", 8.percent do false end
31
+ end
32
+
33
+ foo = Foo.new
34
+ foo.complete?.should be_false
35
+ end
36
+
37
+ it "should evaluate the condition for self" do
38
+ class Foo
39
+ include Zitdunyet::Completeness
40
+ checkoff "Step One", 60.units do |s| s.blork end
41
+ checkoff "Step Two", 40.units do |s| s.count >= 3 end
42
+
43
+ def blork
44
+ true
45
+ end
46
+
47
+ def count
48
+ 3
49
+ end
50
+ end
51
+
52
+ foo = Foo.new
53
+ foo.complete?.should be_true
54
+ foo.percent_complete.should == 100
55
+ end
56
+ end
57
+
58
+ context "Percentages" do
59
+
60
+ it "should evaluate 0 percent when no checkoff items are done" do
61
+ class Foo
62
+ include Zitdunyet::Completeness
63
+ checkoff "Step One", 60.percent do false end
64
+ checkoff "Step Two", 40.percent do false end
65
+ end
66
+
67
+ foo = Foo.new
68
+ foo.percent_complete.should == 0
69
+ end
70
+
71
+ it "should evaluate intermediate percentage when some checkoff items are done" do
72
+ class Foo
73
+ include Zitdunyet::Completeness
74
+ checkoff "Step One", 60.percent do true end
75
+ checkoff "Step Two", 40.percent do false end
76
+ end
77
+
78
+ foo = Foo.new
79
+ foo.percent_complete.should == 60
80
+ end
81
+
82
+ it "should evaluate 100 percent when all checkoff items are done" do
83
+ class Foo
84
+ include Zitdunyet::Completeness
85
+ checkoff "Step One", 60.percent do true end
86
+ checkoff "Step Two", 40.percent do true end
87
+ end
88
+
89
+ foo = Foo.new
90
+ foo.percent_complete.should == 100
91
+ end
92
+
93
+ it "should evaluate fractional percentage when specified" do
94
+ class Foo
95
+ include Zitdunyet::Completeness
96
+ checkoff "Step One", 60.5.percent do true end
97
+ checkoff "Step Two", 39.5.percent do false end
98
+ end
99
+
100
+ foo = Foo.new
101
+ foo.percent_complete.should == 60.5
102
+ end
103
+
104
+ it "should scale the percentage when the specified percentages don't total 100" do
105
+ class Foo
106
+ include Zitdunyet::Completeness
107
+ checkoff "Step One", 40.percent do true end
108
+ checkoff "Step Two", 40.percent do false end
109
+ end
110
+
111
+ foo = Foo.new
112
+ foo.percent_complete.should == 50
113
+ end
114
+
115
+ it "should scale the percentage when the specified percentages exceed 100" do
116
+ pending "a decision on how to handle excess percentage"
117
+ class Foo
118
+ include Zitdunyet::Completeness
119
+ checkoff "Step One", 60.percent do true end
120
+ end
121
+
122
+ class Baz < Foo
123
+ include Zitdunyet::Completeness
124
+ checkoff "Step Two", 60.percent do false end
125
+ end
126
+
127
+ baz = Baz.new
128
+ baz.percent_complete.should == 50
129
+ end
130
+
131
+ end
132
+
133
+ context "Units" do
134
+
135
+ it "should evaluate 0 percent when no checkoff items are done" do
136
+ class Foo
137
+ include Zitdunyet::Completeness
138
+ checkoff "Step One", 60.units do false end
139
+ checkoff "Step Two", 40.units do false end
140
+ end
141
+
142
+ foo = Foo.new
143
+ foo.percent_complete.should == 0
144
+ end
145
+
146
+ it "should evaluate intermediate percentage when some checkoff items are done" do
147
+ class Foo
148
+ include Zitdunyet::Completeness
149
+ checkoff "Step One", 60.units do true end
150
+ checkoff "Step Two", 40.units do false end
151
+ end
152
+
153
+ foo = Foo.new
154
+ foo.percent_complete.should == 60
155
+ end
156
+
157
+ it "should evaluate 100 percent when all checkoff items are done" do
158
+ class Foo
159
+ include Zitdunyet::Completeness
160
+ checkoff "Step One", 60.units do true end
161
+ checkoff "Step Two", 40.units do true end
162
+ end
163
+
164
+ foo = Foo.new
165
+ foo.percent_complete.should == 100
166
+ end
167
+
168
+ it "should evaluate to fractional percents when required" do
169
+ class Foo
170
+ include Zitdunyet::Completeness
171
+ checkoff "Step One", 1.units do true end
172
+ checkoff "Step Two", 199.units do false end
173
+ end
174
+
175
+ foo = Foo.new
176
+ foo.percent_complete.should == 0.5
177
+ end
178
+ end
179
+
180
+ context "Mix of Percent and Units" do
181
+
182
+ it "should evaluate 0 percent when no checkoff items are done" do
183
+ class Foo
184
+ include Zitdunyet::Completeness
185
+ checkoff "Step One", 60.percent do false end
186
+ checkoff "Step Two", 40.units do false end
187
+ end
188
+
189
+ foo = Foo.new
190
+ foo.percent_complete.should == 0
191
+ end
192
+
193
+ it "should evaluate intermediate percentage when some checkoff items are done" do
194
+ class Foo
195
+ include Zitdunyet::Completeness
196
+ checkoff "Step One", 60.percent do true end
197
+ checkoff "Step Two", 40.units do false end
198
+ end
199
+
200
+ foo = Foo.new
201
+ foo.percent_complete.should == 60
202
+ end
203
+
204
+ it "should evaluate scale the units to fit remaining percentage" do
205
+ class Foo
206
+ include Zitdunyet::Completeness
207
+ checkoff "Step One", 60.units do true end
208
+ checkoff "Step Two", 40.percent do false end
209
+ end
210
+
211
+ foo = Foo.new
212
+ foo.percent_complete.should == 60
213
+ end
214
+
215
+ it "should evaluate 100 percent when all checkoff items are done" do
216
+ class Foo
217
+ include Zitdunyet::Completeness
218
+ checkoff "Step One", 60.percent do true end
219
+ checkoff "Step Two", 40.units do true end
220
+ end
221
+
222
+ foo = Foo.new
223
+ foo.percent_complete.should == 100
224
+ end
225
+
226
+ it "should evaluate to fractional percents when required" do
227
+ class Foo
228
+ include Zitdunyet::Completeness
229
+ checkoff "Step One", 1.units do true end
230
+ checkoff "Step Two", 1.units do false end
231
+ checkoff "Step Three", 99.percent do false end
232
+ end
233
+
234
+ foo = Foo.new
235
+ foo.percent_complete.should == 0.5
236
+ end
237
+ end
238
+
239
+ context "Hints" do
240
+
241
+ it "should provide a hint and percentage for uncompleted checkoff items" do
242
+ class Foo
243
+ include Zitdunyet::Completeness
244
+ checkoff "Step One", 60.units do false end
245
+ checkoff "Step Two", 40.units, hint: "Add a widget" do false end
246
+ end
247
+
248
+ foo = Foo.new
249
+ foo.hints.should have(2).items
250
+ one = foo.hints.delete("Step One")
251
+ one.should == 60
252
+ two = foo.hints.delete("Add a widget")
253
+ two.should == 40
254
+ end
255
+
256
+ it "should evaluate a hint when it is callable" do
257
+ class Foo
258
+ include Zitdunyet::Completeness
259
+ checkoff "Step One", 60.units, hint: lambda {|s| s.blork} do false end
260
+ checkoff "Step Two", 40.units, hint: lambda {|s| "Add #{5-s.count} widgets"} do false end
261
+
262
+ def blork
263
+ "Raise the blork coefficient by 30%"
264
+ end
265
+
266
+ def count
267
+ 3
268
+ end
269
+ end
270
+
271
+ foo = Foo.new
272
+ foo.hints.should have(2).items
273
+ puts foo.hints.inspect
274
+ one = foo.hints.delete("Raise the blork coefficient by 30%")
275
+ one.should == 60
276
+ two = foo.hints.delete("Add 2 widgets")
277
+ two.should == 40
278
+ end
279
+
280
+ end
281
+
282
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Expressions" do
4
+
5
+ before(:each) do
6
+ begin
7
+ Object.send(:remove_const, :Foo)
8
+ rescue
9
+ end
10
+ end
11
+
12
+ context "Basics" do
13
+ it "should accept a minimal expression" do
14
+ class Foo
15
+ include Zitdunyet::Completeness
16
+ checkoff "Step One", 8.percent do true end
17
+ end
18
+
19
+ Foo.checklist.first.label.should == "Step One"
20
+ Foo.checklist.first.percent.should == 8
21
+ end
22
+ end
23
+
24
+ context "Percentages" do
25
+
26
+ it "should reject percentage exceeding 100" do
27
+ expect {
28
+ class Foo
29
+ include Zitdunyet::Completeness
30
+ checkoff "Step One", 101.percent do true end
31
+ end
32
+ }.to raise_error(RangeError)
33
+ end
34
+
35
+ it "should reject cumulative percentage exceeding 100" do
36
+ expect {
37
+ class Foo
38
+ include Zitdunyet::Completeness
39
+ checkoff "Step One", 8.percent do true end
40
+ checkoff "Step Two", 93.percent do true end
41
+ end
42
+ }.to raise_error(RangeError)
43
+ end
44
+
45
+ it "should accept percentage exactly 100" do
46
+ class Foo
47
+ include Zitdunyet::Completeness
48
+ checkoff "Step One", 100.percent do true end
49
+ end
50
+ end
51
+
52
+ it "should accept cumulative percentage exactly 100" do
53
+ class Foo
54
+ include Zitdunyet::Completeness
55
+ checkoff "Step One", 8.percent do true end
56
+ checkoff "Step Two", 92.percent do true end
57
+ end
58
+ end
59
+ end
60
+
61
+ context "Hints" do
62
+ it "should accept a hint" do
63
+ class Foo
64
+ include Zitdunyet::Completeness
65
+ checkoff "Step One", 8.percent, hint: "Add a widget" do true end
66
+ end
67
+
68
+ Foo.checklist.first.hint.should == "Add a widget"
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,10 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require "zitdunyet"
5
+ #require "factory_girl"
6
+ #require File.join(File.dirname(__FILE__), "support", "models", "base.rb")
7
+ #Dir.glob(File.join(File.dirname(__FILE__), "support", "**", "*.rb")).each {|f| require f}
8
+ #
9
+ ## TODO: (dcp) why isn't the file loading automatically? should load from spec/factories/*.rb by default.
10
+ #Dir.glob(File.join(File.dirname(__FILE__), "factories", "**", "*.rb")).each {|f| require f}
data/zitdunyet.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/zitdunyet/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "zitdunyet"
6
+ gem.version = Zitdunyet::VERSION
7
+ gem.authors = ["David Pellegrini"]
8
+ gem.email = ["david.pellegrini@spoke.com"]
9
+ gem.description = %q{Evaluate how done or complete a process or entity is.}
10
+ gem.summary = %q{DSL to describe the steps to completion and evaluation engine to compute done-ness.}
11
+ gem.homepage = ""
12
+
13
+ gem.rubyforge_project = "zitdunyet"
14
+
15
+ gem.files = `git ls-files`.split($\)
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "rake"
21
+ gem.add_development_dependency "rspec"
22
+ gem.add_development_dependency "factory_girl"
23
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zitdunyet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - David Pellegrini
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-07-18 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rake
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: factory_girl
39
+ requirement: &id003 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *id003
48
+ description: Evaluate how done or complete a process or entity is.
49
+ email:
50
+ - david.pellegrini@spoke.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - LICENSE
61
+ - README.md
62
+ - Rakefile
63
+ - lib/zitdunyet.rb
64
+ - lib/zitdunyet/checkoff.rb
65
+ - lib/zitdunyet/class_specific.rb
66
+ - lib/zitdunyet/completeness.rb
67
+ - lib/zitdunyet/evaluation.rb
68
+ - lib/zitdunyet/expressions.rb
69
+ - lib/zitdunyet/percent.rb
70
+ - lib/zitdunyet/unit.rb
71
+ - lib/zitdunyet/version.rb
72
+ - spec/lib/zitdunyet/checkoff_spec.rb
73
+ - spec/lib/zitdunyet/class_attr_spec.rb
74
+ - spec/lib/zitdunyet/evaluations_spec.rb
75
+ - spec/lib/zitdunyet/expressions_spec.rb
76
+ - spec/spec_helper.rb
77
+ - zitdunyet.gemspec
78
+ homepage: ""
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: -697234430046674194
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: -697234430046674194
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project: zitdunyet
107
+ rubygems_version: 1.8.24
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: DSL to describe the steps to completion and evaluation engine to compute done-ness.
111
+ test_files:
112
+ - spec/lib/zitdunyet/checkoff_spec.rb
113
+ - spec/lib/zitdunyet/class_attr_spec.rb
114
+ - spec/lib/zitdunyet/evaluations_spec.rb
115
+ - spec/lib/zitdunyet/expressions_spec.rb
116
+ - spec/spec_helper.rb