summary_judgement 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
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
@@ -0,0 +1,7 @@
1
+ = summary_judgement
2
+
3
+ Constructs adaptive summaries of object hierarchies based on ActiveRecord associations and other simple relationship structures.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 Andy Rossmeissl. See LICENSE for details.
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,10 @@
1
+ module SummaryJudgement
2
+ class Descriptor
3
+ attr_reader :phrase, :condition
4
+
5
+ def initialize(phrase, options = {})
6
+ @phrase = phrase
7
+ @condition = options[:if]
8
+ end
9
+ end
10
+ 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