summary_judgement 1.0.0
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/summary_judgement/core_extensions.rb +13 -0
- data/lib/summary_judgement/descriptor.rb +10 -0
- data/lib/summary_judgement/instance_methods.rb +71 -0
- data/lib/summary_judgement/summary.rb +58 -0
- data/lib/summary_judgement.rb +18 -0
- data/test/helper.rb +81 -0
- data/test/test_summary_judgement.rb +73 -0
- metadata +69 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Andy Rossmeissl
|
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.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "summary_judgement"
|
8
|
+
gem.summary = %Q{Dynamic summaries of rich object hierarchies}
|
9
|
+
gem.description = %Q{Constructs adaptive summaries of object hierarchies based on ActiveRecord associations and other simple relationship structures}
|
10
|
+
gem.email = "andy@rossmeissl.net"
|
11
|
+
gem.homepage = "http://github.com/rossmeissl/summary_judgement"
|
12
|
+
gem.authors = ["Andy Rossmeissl"]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/test_*.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "summary_judgement #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class String
|
2
|
+
def indefinite_article
|
3
|
+
%w(a e i o u).include?(first.downcase) ? 'an' : 'a'
|
4
|
+
end
|
5
|
+
|
6
|
+
def with_indefinite_article(upcase = false)
|
7
|
+
"#{upcase ? indefinite_article.humanize : indefinite_article}#{ ' ' unless self.blank? }#{self}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def pluralize_on(qty)
|
11
|
+
qty.is_a?(Numeric) and qty > 1 ? pluralize : self
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module SummaryJudgement
|
2
|
+
module InstanceMethods
|
3
|
+
def summary(options = {})
|
4
|
+
if children
|
5
|
+
summarize_as_branch(options)
|
6
|
+
elsif term
|
7
|
+
summarize_as_leaf(options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def term
|
12
|
+
self.class.summary.class.render self.class.summary.term, self
|
13
|
+
end
|
14
|
+
|
15
|
+
def adjectives
|
16
|
+
self.class.summary.adjectives.select { |a| a.condition.nil? or self.class.summary.class.render(a.condition, self) }.map { |a| self.class.summary.class.render a.phrase, self}
|
17
|
+
end
|
18
|
+
|
19
|
+
def modifiers
|
20
|
+
self.class.summary.modifiers.select { |m| m.condition.nil? or self.class.summary.class.render(m.condition, self) }.map { |m| self.class.summary.class.render m.phrase, self}
|
21
|
+
end
|
22
|
+
|
23
|
+
def children
|
24
|
+
self.class.summary.class.render self.class.summary.subordinates, self
|
25
|
+
end
|
26
|
+
|
27
|
+
def canopy
|
28
|
+
branches, leaves = children.flatten.partition(&:children)
|
29
|
+
(leaves + branches.collect(&:canopy)).flatten.uniq
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def summarize_as_leaf(options = {})
|
35
|
+
options.reverse_merge! :capitalize_indefinite_article => true
|
36
|
+
"#{adjectives.join(' ').strip.with_indefinite_article(options[:capitalize_indefinite_article])} #{term} #{modifiers.join(' ')}".strip
|
37
|
+
end
|
38
|
+
|
39
|
+
def summarize_as_branch(options = {})
|
40
|
+
result = ''
|
41
|
+
if conjugation = options.delete(:conjugate)
|
42
|
+
options.reverse_merge! :tense => :present, :plurality => :singular
|
43
|
+
case conjugation
|
44
|
+
when String
|
45
|
+
options[:person] ||= :third
|
46
|
+
options[:subject] ||= conjugation
|
47
|
+
when Symbol
|
48
|
+
options[:person] ||= conjugation
|
49
|
+
when TrueClass
|
50
|
+
options[:person] ||= :third
|
51
|
+
end
|
52
|
+
result << Verbs::Conjugator.conjugate(self.class.summary.predicate, options.slice(:person, :subject, :tense, :plurality)).to_s.humanize
|
53
|
+
result << ' '
|
54
|
+
end
|
55
|
+
|
56
|
+
verbosity = options.delete(:verbose)
|
57
|
+
verbosity = send(verbosity) if verbosity.is_a?(Symbol)
|
58
|
+
|
59
|
+
if verbosity
|
60
|
+
leaves = canopy
|
61
|
+
first_leaf = leaves.shift
|
62
|
+
result << leaves.map { |leaf| leaf.summary :capitalize_indefinite_article => false }.unshift(first_leaf.summary(:capitalize_indefinite_article => !conjugation)).to_sentence
|
63
|
+
else
|
64
|
+
result << canopy.map {|c| c.class}.uniq.map do |k|
|
65
|
+
siblings = canopy.select {|c| c.is_a? k}
|
66
|
+
siblings.length.to_s + ' ' + k.to_s.underscore.humanize.downcase.pluralize_on(siblings.length)
|
67
|
+
end.to_sentence
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SummaryJudgement
|
2
|
+
class Summary
|
3
|
+
attr_reader :term, :adjectives, :modifiers, :subordinates, :base
|
4
|
+
|
5
|
+
def initialize(base, &blk)
|
6
|
+
@base = base
|
7
|
+
@term = base.to_s
|
8
|
+
@adjectives = []
|
9
|
+
@modifiers = []
|
10
|
+
@subordinates = []
|
11
|
+
yield self
|
12
|
+
end
|
13
|
+
|
14
|
+
def identity(t)
|
15
|
+
@term = t
|
16
|
+
end
|
17
|
+
|
18
|
+
def adjective(a, options = {})
|
19
|
+
@adjectives << SummaryJudgement::Descriptor.new(a, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def modifier(m, options = {})
|
23
|
+
@modifiers << SummaryJudgement::Descriptor.new(m, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def children(collection_or_symbol)
|
27
|
+
case collection_or_symbol
|
28
|
+
when Symbol
|
29
|
+
@subordinates << lambda { |parent| parent.send collection_or_symbol }
|
30
|
+
else
|
31
|
+
@subordinates << collection_or_symbol
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def verb(infinitive)
|
36
|
+
@verb = infinitive
|
37
|
+
end
|
38
|
+
|
39
|
+
def predicate
|
40
|
+
@verb
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def render(obj, context)
|
45
|
+
case obj
|
46
|
+
when Array
|
47
|
+
obj.empty? ? nil : obj.map {|o| render o, context}
|
48
|
+
when String
|
49
|
+
obj
|
50
|
+
when Symbol
|
51
|
+
context.send obj
|
52
|
+
when Proc
|
53
|
+
obj.call context
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'verbs'
|
3
|
+
|
4
|
+
module SummaryJudgement
|
5
|
+
def summarize(&blk)
|
6
|
+
@summary = Summary.new(self, &blk)
|
7
|
+
send :include, InstanceMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
def summary
|
11
|
+
@summary
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'summary_judgement/summary'
|
16
|
+
require 'summary_judgement/descriptor'
|
17
|
+
require 'summary_judgement/instance_methods'
|
18
|
+
require 'summary_judgement/core_extensions'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
require 'summary_judgement'
|
7
|
+
|
8
|
+
class Test::Unit::TestCase
|
9
|
+
end
|
10
|
+
|
11
|
+
class Book
|
12
|
+
attr_reader :book_type, :author, :color, :length_in_pages, :pictures
|
13
|
+
def initialize(options = {})
|
14
|
+
@book_type = options[:book_type]
|
15
|
+
@author = options[:author]
|
16
|
+
@color = options[:color]
|
17
|
+
@length_in_pages = options[:length_in_pages]
|
18
|
+
@pictures = options[:pictures]
|
19
|
+
end
|
20
|
+
|
21
|
+
extend SummaryJudgement
|
22
|
+
summarize do |has|
|
23
|
+
has.adjective :color
|
24
|
+
has.adjective 'illustrated', :if => :pictures
|
25
|
+
has.adjective lambda { |book| "#{book.length_in_pages}pp" }, :if => :length_in_pages
|
26
|
+
has.identity lambda { |book| book.book_type }
|
27
|
+
has.modifier lambda { |book| "by #{book.author}" }, :if => :author
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Magazine
|
32
|
+
attr_reader :length_in_pages, :year, :month
|
33
|
+
def initialize(options = {})
|
34
|
+
@length_in_pages = options[:length_in_pages]
|
35
|
+
@year = options[:year]
|
36
|
+
@month = options[:month]
|
37
|
+
end
|
38
|
+
|
39
|
+
def date
|
40
|
+
"#{month} #{year}".strip
|
41
|
+
end
|
42
|
+
|
43
|
+
extend SummaryJudgement
|
44
|
+
summarize do |has|
|
45
|
+
has.adjective lambda { |magazine| "#{magazine.length_in_pages}pp" }, :if => :length_in_pages
|
46
|
+
has.identity 'magazine issue'
|
47
|
+
has.modifier lambda { |magazine| "from #{magazine.date}" }, :if => :date
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Library
|
52
|
+
attr_reader :books, :magazines
|
53
|
+
def initialize(*volumes)
|
54
|
+
@books = volumes.select {|v| v.is_a? Book}
|
55
|
+
@magazines = volumes.select {|v| v.is_a? Magazine}
|
56
|
+
end
|
57
|
+
|
58
|
+
def small?
|
59
|
+
@books.length + @magazines.length < 2
|
60
|
+
end
|
61
|
+
|
62
|
+
extend SummaryJudgement
|
63
|
+
summarize do |has|
|
64
|
+
has.children :books
|
65
|
+
has.children :magazines
|
66
|
+
has.verb :have
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Catalog
|
71
|
+
attr_reader :libraries
|
72
|
+
def initialize(*libraries)
|
73
|
+
@libraries = libraries
|
74
|
+
end
|
75
|
+
|
76
|
+
extend SummaryJudgement
|
77
|
+
summarize do |has|
|
78
|
+
has.children :libraries
|
79
|
+
has.verb :contain
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestSummaryJudgement < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@neuromancer = Book.new :book_type => 'novel', :author => 'William Gibson'
|
6
|
+
@where_the_wild_things_are = Book.new :book_type => 'childrens book', :author => 'Maurice Sendak', :pictures => true
|
7
|
+
@current_economist = Magazine.new :year => 2009, :month => "December"
|
8
|
+
@bookshelf = Library.new @neuromancer, @where_the_wild_things_are
|
9
|
+
@toilet = Library.new @neuromancer, @current_economist
|
10
|
+
@bedstand = Library.new @neuromancer
|
11
|
+
@catalog = Catalog.new @bookshelf, @toilet
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_setup
|
15
|
+
assert_equal Book, @neuromancer.class
|
16
|
+
assert_equal Library, @bookshelf.class
|
17
|
+
assert_equal 2, @bookshelf.books.length
|
18
|
+
assert_equal :accept, Verbs::Conjugator.conjugate(:accept, :tense => :present, :person => :first, :plurality => :singular)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_summary_definition
|
22
|
+
assert_equal SummaryJudgement::Summary, Book.summary.class
|
23
|
+
assert_equal Proc, Book.summary.term.class
|
24
|
+
assert_equal [SummaryJudgement::Descriptor], Book.summary.adjectives.collect { |a| a.class }.uniq
|
25
|
+
assert_equal :have, Library.summary.predicate
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_foliage
|
29
|
+
assert_equal 3, @catalog.canopy.length
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_leaf_summary_rendering
|
33
|
+
assert_equal 'novel', @neuromancer.term
|
34
|
+
assert @where_the_wild_things_are.adjectives.include?('illustrated')
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_association
|
38
|
+
assert_equal 2, @bookshelf.children.length
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_summary
|
42
|
+
assert_equal 'An illustrated childrens book by Maurice Sendak', @where_the_wild_things_are.summary
|
43
|
+
assert_equal '2 books', @bookshelf.summary
|
44
|
+
assert_equal '1 book and 1 magazine', @toilet.summary
|
45
|
+
assert_equal '2 books and 1 magazine', @catalog.summary
|
46
|
+
assert_equal 'A novel by William Gibson', @neuromancer.summary
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_conjugated_summary
|
50
|
+
assert_equal 'Has 2 books', @bookshelf.summary(:conjugate => true, :subject => false)
|
51
|
+
assert_equal 'You have 2 books', @bookshelf.summary(:conjugate => :second, :subject => true)
|
52
|
+
assert_equal 'The catalog contains 2 books and 1 magazine', @catalog.summary(:conjugate => :third, :subject => 'The catalog')
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_verbose_summaries
|
56
|
+
assert_equal 'A novel by William Gibson and an illustrated childrens book by Maurice Sendak', @bookshelf.summary(:verbose => true)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_conjugated_verbose_summaries
|
60
|
+
assert_equal 'I have a novel by William Gibson and an illustrated childrens book by Maurice Sendak', @bookshelf.summary(:verbose => true, :conjugate => :first, :subject => true)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_summaries_with_adaptive_verbosity
|
64
|
+
assert_equal '2 books', @bookshelf.summary(:verbose => :small?)
|
65
|
+
assert_equal 'A novel by William Gibson', @bedstand.summary(:verbose => :small?)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_conjugated_summaries_with_adaptive_verbosity
|
69
|
+
assert_equal 'They have 2 books', @bookshelf.summary(:verbose => :small?, :conjugate => :third, :plurality => :plural, :subject => true)
|
70
|
+
assert_equal 'They have a novel by William Gibson', @bedstand.summary(:verbose => :small?, :conjugate => :third, :plurality => :plural, :subject => true)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: summary_judgement
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andy Rossmeissl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-03 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Constructs adaptive summaries of object hierarchies based on ActiveRecord associations and other simple relationship structures
|
17
|
+
email: andy@rossmeissl.net
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- VERSION
|
32
|
+
- lib/summary_judgement.rb
|
33
|
+
- lib/summary_judgement/core_extensions.rb
|
34
|
+
- lib/summary_judgement/descriptor.rb
|
35
|
+
- lib/summary_judgement/instance_methods.rb
|
36
|
+
- lib/summary_judgement/summary.rb
|
37
|
+
- test/helper.rb
|
38
|
+
- test/test_summary_judgement.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/rossmeissl/summary_judgement
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --charset=UTF-8
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.5
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Dynamic summaries of rich object hierarchies
|
67
|
+
test_files:
|
68
|
+
- test/helper.rb
|
69
|
+
- test/test_summary_judgement.rb
|