tender_summary 1.1.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 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 Eric Lindvall
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,51 @@
1
+ = Tender Summary
2
+
3
+ Use this with cron or launchd to send summary emails of your Tender
4
+ discussions.
5
+
6
+ It will include all pending discussions in the email.
7
+
8
+
9
+ == Installation
10
+
11
+ To install, just run:
12
+
13
+ $ sudo gem install tender_summary
14
+
15
+
16
+ == Usage
17
+
18
+ An example crontab entry:
19
+
20
+ 0 9 * * * tender_summary -s tenderaccount -u robot@tenderaccount.com -p XXXX6zxY7b -t eric@tenderaccount.com -f 'Support <support@tenderaccount.com>'
21
+
22
+ Note: The password for the account will be visible in the system process
23
+ table when the program is running.
24
+
25
+
26
+ == Inspiration
27
+
28
+ The styling used and inspiration was taken from the
29
+ {Basecamp}[http://www.basecamphq.com] daily summary emails. They look
30
+ really nice.
31
+
32
+
33
+ == TODO
34
+
35
+ * Add command line option for customized view
36
+
37
+
38
+ == Note on Patches/Pull Requests
39
+
40
+ * Fork the project.
41
+ * Make your feature addition or bug fix.
42
+ * Add tests for it. This is important so I don't break it in a
43
+ future version unintentionally.
44
+ * Commit, do not mess with rakefile, version, or history.
45
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
46
+ * Send me a pull request. Bonus points for topic branches.
47
+
48
+
49
+ == Copyright
50
+
51
+ Copyright (c) 2009 Eric Lindvall. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "tender_summary"
8
+ gem.summary = %Q{A simple tool to email your pending Tender discussions}
9
+ gem.description = %Q{A simple tool to email your pending Tender discussions.}
10
+ gem.email = "eric@sevenscale.com"
11
+ gem.homepage = "http://github.com/eric/tender_summary"
12
+ gem.authors = ["Eric Lindvall"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_development_dependency "yard", ">= 0"
15
+ gem.add_dependency 'httparty', '~> 0.4.5'
16
+ gem.add_dependency 'addressable', '~> 2.1.1'
17
+ gem.add_dependency 'actionmailer', '~> 2.3.5'
18
+
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
24
+ end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/**/test_*.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => :check_dependencies
47
+
48
+ task :default => :test
49
+
50
+ begin
51
+ require 'yard'
52
+ YARD::Rake::YardocTask.new
53
+ rescue LoadError
54
+ task :yardoc do
55
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
56
+ end
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tender_summary'
4
+
5
+ TenderSummary::Cli.new(ARGV).run
@@ -0,0 +1,8 @@
1
+
2
+ require 'addressable/template'
3
+ require 'action_mailer'
4
+ require 'httparty'
5
+ require 'optparse'
6
+ require 'tender_summary/cli'
7
+ require 'tender_summary/tender_api'
8
+ require 'tender_summary/mailer'
@@ -0,0 +1,59 @@
1
+
2
+ module TenderSummary
3
+ class Cli
4
+ attr_accessor :to, :from
5
+
6
+ def initialize(argv)
7
+ @argv = argv.dup
8
+ end
9
+
10
+ def run
11
+ self.parse
12
+
13
+ TenderSummary::Mailer.deliver_pending(self.to, self.from)
14
+ end
15
+
16
+ def parse
17
+ opts = OptionParser.new do |opts|
18
+ opts.banner = "Usage: #{File.basename($0)} [options]"
19
+
20
+ opts.separator ' '
21
+ opts.separator 'Specific options:'
22
+
23
+ opts.on("-s", "--subdomain SUBDOMAIN", "Tender subdomain") do |n|
24
+ TenderSummary::TenderApi.subdomain = n
25
+ end
26
+
27
+ opts.on("-u", "--username USERNAME", "Tender username") do |n|
28
+ TenderSummary::TenderApi.username = n
29
+ end
30
+
31
+ opts.on("-p", "--password PASSWORD", "Tender password") do |n|
32
+ TenderSummary::TenderApi.password = n
33
+ end
34
+
35
+ opts.on("-t", "--to a,b,c", Array, "Users to email") do |n|
36
+ self.to = n
37
+ end
38
+
39
+ opts.on("-f", "--from FROM", "Email address to use for From") do |n|
40
+ self.from = n
41
+ end
42
+
43
+ opts.separator " "
44
+ opts.separator "Common options:"
45
+
46
+ # No argument, shows at tail. This will print an options summary.
47
+ # Try it and see!
48
+ opts.on_tail("-h", "--help", "Show this message") do
49
+ puts opts
50
+ exit
51
+ end
52
+ end
53
+
54
+ opts.parse!(@argv)
55
+
56
+ TenderSummary::TenderApi.authenticate
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,18 @@
1
+ module TenderSummary
2
+ class Mailer < ActionMailer::Base
3
+ self.template_root = File.expand_path("#{File.dirname(__FILE__)}/templates")
4
+ self.mailer_name = 'mailer'
5
+ self.delivery_method = :sendmail
6
+
7
+ def pending(to, from = nil)
8
+ discussions = TenderSummary::TenderApi.discussions(:state => :pending)
9
+
10
+ from from
11
+ recipients to
12
+ subject "Tender pending discussions summary"
13
+ body :discussions => discussions['discussions'],
14
+ :site => TenderSummary::TenderApi.site,
15
+ :name => TenderSummary::TenderApi.site['name']
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,91 @@
1
+ <div style="background-color: rgb(232, 232, 232); text-align: left;" vlink="#0099cc" alink="#0099cc" link="#0099cc" bgcolor="#e8e8e8">
2
+
3
+ <center>
4
+
5
+ <table cellspacing="0" cellpadding="0" border="0" width="700">
6
+ <tbody><tr>
7
+ <td style="padding-top: 20px; padding-bottom: 20px; text-align: left;">
8
+ <center>
9
+ <table cellspacing="0" cellpadding="0" border="0" width="650">
10
+ <tbody><tr>
11
+ <td style="padding: 0 20px 20px 20px; font-family: Helvetica,sans-serif; font-size: 16px; background-color: rgb(255, 255, 255); text-align: left;">
12
+
13
+ <h1 style="font-family: Helvetica,sans-serif; font-size: 28px; color: rgb(51, 51, 51); margin-bottom: 0pt; font-weight: bold;">Your Daily Tender Digest for <a target="blank" href="<%= @site.href(:html)%>"><%= @name %></a></h1>
14
+
15
+ <h2 style="margin: 6px 0pt 0pt; font-family: Helvetica,sans-serif; font-size: 16px; color: rgb(119, 119, 119); font-weight: normal;">
16
+ Discussions marked as pending
17
+ </h2>
18
+ </td>
19
+ </tr>
20
+
21
+ <tr>
22
+ <td style="border-top: 1px solid rgb(204, 204, 204); padding: 20px; font-family: Helvetica,sans-serif; font-size: 14px; background-color: rgb(255, 255, 255); text-align: left;">
23
+ <p style="color: rgb(204, 51, 51); font-weight: bold; font-size: 16px; margin-bottom: 10px; padding-bottom: 0pt;">Pending discussions:</p>
24
+
25
+ <table>
26
+ <tbody><tr>
27
+ <td style="padding-left: 20px;">
28
+
29
+ <%- @discussions.each do |discussion| -%>
30
+ <p style="font-weight: normal; font-size: 14px; color: rgb(0, 0, 0); margin-bottom: 2px; padding-bottom: 0pt;">
31
+ <span style="margin:0 3px; padding:2px 7px 3px 7px; font-size:10px; font-weight:normal; background:#eee; color:#666; text-transform:uppercase; -webkit-border-radius:3px; -moz-border-radius:2px; white-space:nowrap;">
32
+ <%= discussion['state'] %>
33
+ </span>
34
+
35
+ <a target="_blank" href="<%= discussion.href(:html) %>"><%= discussion["title"] %></a>
36
+ </p>
37
+ <table>
38
+ <tbody><tr>
39
+ <td style="vertical-align: top; padding-right: 3px;"><span style="font-size: 24px; line-height: 18px;">•</span></td>
40
+ <td style="font-size: 14px; vertical-align: top; padding-bottom: 3px;">
41
+ From <%= discussion['last_author_name']%> &lt;<%= discussion['last_author_email'] %>&gt;
42
+
43
+ <p style="margin: 0pt; font-weight: normal; color: rgb(119, 119, 119); font-size: 12px;">
44
+
45
+ Last updated
46
+ <strong><%= discussion['last_updated_at'].localtime.to_s(:short) %></strong>
47
+
48
+ <%- if discussion['public'] -%>
49
+ <strong>Discussion is public</strong>
50
+ <%- else -%>
51
+ Discussion is private
52
+ <%- end -%>
53
+ </p>
54
+ </td>
55
+ </tr>
56
+
57
+ </tbody></table>
58
+ <%- end -%>
59
+
60
+ </td>
61
+ </tr>
62
+ </tbody></table>
63
+
64
+ </td>
65
+ </tr>
66
+
67
+ <!--
68
+ <tr>
69
+ <td style="border-top: 1px solid rgb(204, 204, 204); padding: 20px 20px 30px; font-family: Helvetica,sans-serif; font-size: 14px; background-color: rgb(255, 255, 255); text-align: left;">
70
+ <p><a target="_blank" style="font-size: 12px; color: rgb(102, 102, 102);" href="#">Unsubscribe to stop receiving updates</a></p>
71
+ </td>
72
+ </tr>
73
+ </tbody></table>
74
+ -->
75
+
76
+ <table cellspacing="0" cellpadding="0" border="0" width="650">
77
+ <tbody><tr>
78
+ <td style="padding-top: 10px; padding-bottom: 30px; font-size: 10px; font-family: Verdana,sans-serif; color: rgb(102, 102, 102); text-align: left;">
79
+
80
+ </td>
81
+ </tr>
82
+ </tbody></table>
83
+
84
+ </center>
85
+ </td>
86
+ </tr>
87
+ </tbody></table>
88
+
89
+ </center>
90
+
91
+ </div>
@@ -0,0 +1,57 @@
1
+ module TenderSummary
2
+ class TenderApi
3
+ include HTTParty
4
+
5
+ headers 'Accept' => 'application/vnd.tender-v1+json'
6
+ format :json
7
+ parser Proc.new { |body| add_json_helpers(Crack::JSON.parse(body)) }
8
+
9
+ class << self
10
+ attr_accessor :subdomain, :username, :password
11
+ end
12
+
13
+ def self.authenticate(subdomain = nil, username = nil, password = nil)
14
+ self.subdomain ||= subdomain
15
+ self.username ||= username
16
+ self.password ||= password
17
+
18
+ basic_auth(self.username, self.password)
19
+ base_uri(HTTParty.normalize_base_uri(site_href))
20
+ end
21
+
22
+ def self.discussions(options = {})
23
+ get(site.href(:discussions, options))
24
+ end
25
+
26
+ def self.site
27
+ @site ||= get(site_href)
28
+ end
29
+
30
+ def self.base
31
+ @base ||= get('https://api.tenderapp.com/')
32
+ end
33
+
34
+ def self.site_href
35
+ @site_href ||= base.href('site', :site_permalink => subdomain)
36
+ end
37
+
38
+ # Make the JSON a litte bit more fun to work with
39
+ def self.add_json_helpers(data)
40
+ case data
41
+ when Hash
42
+ data.send(:extend, TenderSummary::JsonHelpers)
43
+ data.each { |_, value| add_json_helpers(value) }
44
+ when Array
45
+ data.each { |elem| add_json_helpers(elem) }
46
+ end
47
+
48
+ data
49
+ end
50
+ end
51
+
52
+ module JsonHelpers
53
+ def href(key, options = {})
54
+ Addressable::Template.new(self["#{key}_href"]).expand(options).to_s
55
+ end
56
+ end
57
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'tender-summary'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestTenderSummary < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tender_summary
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eric Lindvall
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-19 00:00:00 -08:00
13
+ default_executable: tender_summary
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: httparty
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.4.5
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: addressable
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.1.1
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: actionmailer
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: 2.3.5
64
+ version:
65
+ description: A simple tool to email your pending Tender discussions.
66
+ email: eric@sevenscale.com
67
+ executables:
68
+ - tender_summary
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - LICENSE
73
+ - README.rdoc
74
+ files:
75
+ - .document
76
+ - .gitignore
77
+ - LICENSE
78
+ - README.rdoc
79
+ - Rakefile
80
+ - VERSION
81
+ - bin/tender_summary
82
+ - lib/tender_summary.rb
83
+ - lib/tender_summary/cli.rb
84
+ - lib/tender_summary/mailer.rb
85
+ - lib/tender_summary/templates/mailer/pending.text.html.erb
86
+ - lib/tender_summary/tender_api.rb
87
+ - test/helper.rb
88
+ - test/test_tender-summary.rb
89
+ has_rdoc: true
90
+ homepage: http://github.com/eric/tender_summary
91
+ licenses: []
92
+
93
+ post_install_message:
94
+ rdoc_options:
95
+ - --charset=UTF-8
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "0"
103
+ version:
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: "0"
109
+ version:
110
+ requirements: []
111
+
112
+ rubyforge_project:
113
+ rubygems_version: 1.3.5
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: A simple tool to email your pending Tender discussions
117
+ test_files:
118
+ - test/helper.rb
119
+ - test/test_tender-summary.rb