├── .rspec
├── README
├── autotest
└── discover.rb
├── .gitignore
├── lib
├── load_path.rb
├── helper.rb
├── db_connection.rb
├── mail_lib.rb
├── email.rb
├── post.rb
├── parser.rb
└── aggregator.rb
├── config.ru
├── subjects_example.yml
├── server_example.yml
├── spec
├── fabricators
│ └── post.rb
├── mail_lib_spec.rb
├── spec_helper.rb
├── aggregator_spec.rb
├── post_spec.rb
└── parser_spec.rb
├── mongoid_example.yml
├── Gemfile
├── views
└── index.erb
├── classic.rb
└── Gemfile.lock
/.rspec:
--------------------------------------------------------------------------------
1 | --color
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Filter and parse emails sent from feedmyinbox.com
--------------------------------------------------------------------------------
/autotest/discover.rb:
--------------------------------------------------------------------------------
1 | Autotest.add_discovery { "rspec2" }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | server.yml
2 | mongoid.yml
3 | subjects.yml
4 | coverage/
--------------------------------------------------------------------------------
/lib/load_path.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require File.expand_path('../classic', __FILE__)
2 | run Sinatra::Application
--------------------------------------------------------------------------------
/subjects_example.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - subject1
3 | - subject2
4 | - subject3
5 | - subject4
6 | - subject5
--------------------------------------------------------------------------------
/server_example.yml:
--------------------------------------------------------------------------------
1 | server_type: pop3
2 | server: pop3.live.com
3 | port: 995
4 | user_name: your_user_name@live.com
5 | password: your_password
6 | enable_ssl: true
--------------------------------------------------------------------------------
/spec/fabricators/post.rb:
--------------------------------------------------------------------------------
1 | Fabricator(:post) do
2 | date { Fabricate.sequence(:date) { |i| Date.today - i } }
3 | subject Faker::Lorem.sentence
4 | body Faker::Lorem.sentences
5 | end
--------------------------------------------------------------------------------
/lib/helper.rb:
--------------------------------------------------------------------------------
1 | module Helper
2 |
3 | class << self
4 |
5 | def force_encoding(str)
6 | str = str.unpack('C*').pack('U*')
7 | str
8 | end
9 |
10 | end
11 |
12 | end
--------------------------------------------------------------------------------
/lib/db_connection.rb:
--------------------------------------------------------------------------------
1 | db_config = YAML::load File.open(File.expand_path('../mongoid.yml', File.dirname(__FILE__) ) )
2 | RACK_ENV ||= 'development'
3 | db_name = db_config[RACK_ENV]['database']
4 | Mongoid.configure do |config|
5 | config.master = Mongo::Connection.new.db(db_name)
6 | end
--------------------------------------------------------------------------------
/spec/mail_lib_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'mail_lib'
3 |
4 | describe MailLib do
5 |
6 | it "should set up retriever_method when the module is included" do
7 | cls = Class.new
8 | cls.class_eval do
9 | include MailLib
10 | end
11 | Mail.retriever_method.settings[:user_name].should_not be_nil
12 | Mail.retriever_method.settings[:password].should_not be_nil
13 | end
14 |
15 | end
--------------------------------------------------------------------------------
/mongoid_example.yml:
--------------------------------------------------------------------------------
1 | defaults: &defaults
2 | host: localhost
3 | # slaves:
4 | # - host: slave1.local
5 | # port: 27018
6 | # - host: slave2.local
7 | # port: 27019
8 |
9 | development:
10 | <<: *defaults
11 | database: mail_aggregator_development
12 |
13 | test:
14 | <<: *defaults
15 | database: mail_aggregator_test
16 |
17 | production:
18 | host: <%= ENV['MONGOID_HOST'] %>
19 | port: <%= ENV['MONGOID_PORT'] %>
20 | username: <%= ENV['MONGOID_USERNAME'] %>
21 | password: <%= ENV['MONGOID_PASSWORD'] %>
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 |
3 | # gem 'rails', :git => 'git://github.com/rails/rails.git'
4 | gem 'mail'
5 | gem 'mongoid', :git => 'git://github.com/mongoid/mongoid.git'
6 | gem 'bson_ext'
7 | gem 'fabrication'
8 | gem 'faker'
9 | gem 'sinatra', '1.3.0e'
10 | gem 'padrino-helpers'
11 | gem 'rack-flash'
12 |
13 | group :test do
14 | gem 'rspec'
15 | gem 'simplecov', :require => false
16 | gem 'autotest'
17 | end
18 |
19 | group :development do
20 | gem 'ruby-debug19'
21 | gem 'shotgun'
22 | gem "awesome_print"
23 | end
24 |
--------------------------------------------------------------------------------
/lib/mail_lib.rb:
--------------------------------------------------------------------------------
1 | require 'mail'
2 |
3 | module MailLib
4 |
5 | def self.included(base)
6 | self.setup
7 | end
8 |
9 | def self.setup
10 | config = YAML::load File.open(File.expand_path('../server.yml', File.dirname(__FILE__) ) )
11 | Mail.defaults do
12 | retriever_method config['server_type'], :address => config['server'],
13 | :port => config['port'],
14 | :user_name => config['user_name'],
15 | :password => config['password'],
16 | :enable_ssl => config['enable_ssl']
17 | end
18 | end
19 |
20 | end
--------------------------------------------------------------------------------
/lib/email.rb:
--------------------------------------------------------------------------------
1 | require_relative 'load_path'
2 | require 'mongoid'
3 | require 'parser'
4 | require 'mail_lib'
5 | require 'helper'
6 | require 'post'
7 |
8 | class Email
9 | include Mongoid::Document
10 | include MailLib
11 | extend Parser
12 | field :subject
13 | field :message_id
14 | field :date, type: Date
15 | index :message_id, unique: true
16 |
17 | has_many :posts, :dependent => :destroy
18 |
19 | class << self
20 |
21 | def store(mail)
22 | parent = Email.create(:message_id => mail.message_id, :date => mail.date.to_date, :subject => Helper.force_encoding(mail.subject) )
23 | self.parse(mail) do |post_array|
24 | parent.posts << Post.store(post_array)
25 | end
26 | end
27 |
28 | def has_message_id?(message_id)
29 | !Email.where(:message_id => message_id).empty?
30 | end
31 |
32 | end # end class methods
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2 |
3 | RACK_ENV = 'test'
4 |
5 | require 'rspec'
6 | require 'mongoid'
7 | require 'simplecov'
8 | require 'fabrication'
9 | require 'faker'
10 | require 'email'
11 | require 'post'
12 | require 'db_connection'
13 |
14 | SimpleCov.start
15 |
16 | RSpec.configure do |config|
17 | # config.mock_with :rspec
18 | config.before :each do
19 | Mongoid.master.collections.select {|c| c.name !~ /system/ }.each(&:drop)
20 | end
21 | end
22 |
23 | def mail_body
24 | %{
25 | // xxx xxx\n// xxxx\n Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
26 | }
27 | end
--------------------------------------------------------------------------------
/views/index.erb:
--------------------------------------------------------------------------------
1 |
<%= flash[:notice] %>
2 | <%= flash[:alert] %>
3 | <% @posts.each do |post| %>
4 |
5 | <%= post.date %>
6 | <%= button_to "Hide", "/mark_hide/#{post.id}?page=#{params[:page] || 1}" %>
7 |
8 | <%= post.subject %>
9 | <%= post.body %>
10 | <% form_for post, "/update/#{post.id}" do |f|%>
11 | <%= hidden_field_tag "page", :value => params[:page] %>
12 | <%= f.select(:status, :options => ["new", "open", "close", "low", "high"], :selected => post.status) %>
13 | <%= f.text_area "note", :value => post.note %>
14 | <%= f.submit "Update" %>
15 | <% end %>
16 |
17 |
18 | <% end %>
19 | <% @posts.num_of_page.times do |page| %>
20 | <% unless params[:page].to_i == page + 1 %>
21 | <%= link_to page + 1, "/?page=#{page+1}", :style => "font-size:30px;padding:10px;" %>
22 | <% else %>
23 | <%= page + 1 %>
24 | <% end %>
25 | <% end %>
--------------------------------------------------------------------------------
/lib/post.rb:
--------------------------------------------------------------------------------
1 | require 'mongoid'
2 |
3 | class Post
4 | include Mongoid::Document
5 | include Mongoid::Timestamps
6 | field :date, type: Date
7 | field :subject
8 | field :body
9 | field :visible, type: Boolean, default: true
10 | field :status, default: 'new'
11 | field :note
12 |
13 | # db.posts.ensureIndex({date:1})
14 | index :date# , Mongo::ASCEDING
15 |
16 | scope :visible, where(:visible => true)
17 |
18 | belongs_to :email
19 |
20 | class << self
21 |
22 | def list
23 | Post.visible.order_by(:date)
24 | end
25 |
26 | end # end class methods
27 |
28 | def self.store(post_array)
29 | Post.create(:subject => post_array[0], :date => post_array[1], :body => post_array[2] )
30 | end
31 |
32 | def hide
33 | update_attribute(:visible, false) if visible == true
34 | end
35 |
36 | def show
37 | update_attribute(:visible, true) if visible == false
38 | end
39 |
40 | def change_status(status)
41 | update_attribute(:status, status) if self.status != status
42 | end
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/lib/parser.rb:
--------------------------------------------------------------------------------
1 | module Parser
2 |
3 | attr_reader :mail
4 |
5 | # takes a mail and returns an array of post array
6 | def parse(mail)
7 | @mail = mail
8 | post = []
9 | body = ""
10 | if !mail.body.parts.empty?
11 | get_textual_body_io.each_line do |line|
12 | if line.match(/^\s*\/\/ /)
13 | post << line.gsub(/^\s*\/\/ /, "").chomp if post.length == 0
14 | post << mail.date.to_date if post.length == 1
15 | post << body if post.length == 2 && body.length > 0
16 | if post.length == 3
17 | yield post
18 | post = []
19 | body = ""
20 | post << line.gsub(/^\s*\/\/ /, "").chomp
21 | end
22 | else
23 | body << line.gsub(/\n/, "")
24 | end
25 | end # end each_line
26 | post << body
27 | yield post if post.length == 3
28 | end # end if
29 | end
30 |
31 | private
32 |
33 | def get_textual_body_io
34 | body = Helper.force_encoding(mail.body.parts.first.body.to_s)
35 | StringIO.new(body.split("Create an Account: \nhttps://www.feedmyinbox.com/")[0])
36 | end
37 |
38 | end
--------------------------------------------------------------------------------
/lib/aggregator.rb:
--------------------------------------------------------------------------------
1 | require_relative 'load_path'
2 | require 'mongoid'
3 | require 'db_connection'
4 | require 'email'
5 | require 'logger'
6 |
7 | class Aggregator
8 |
9 | def sync(initial_sync = false)
10 | Mail.find(:order => initial_sync ? :asc : :desc, :count => :all) do |mail|
11 | # avoid incorrect encoding
12 | begin
13 | subject = mail.subject
14 | logger.info "Processing #{subject} sent on #{mail.date}"
15 | rescue ArgumentError, Encoding::UndefinedConversionError # ArgumentError => undefined encoding
16 | subject = ""
17 | end
18 | get_subjects.each do |subject_type|
19 | if subject =~ /#{subject_type}/i
20 | if Email.has_message_id?(mail.message_id)
21 | return unless initial_sync
22 | else
23 | logger.info "*******Storing #{mail.subject}***********"
24 | Email.store(mail)
25 | end
26 | end
27 | end
28 | end
29 | end
30 |
31 | private
32 |
33 | def logger
34 | @logger ||= Logger.new(STDOUT)
35 | end
36 |
37 | def get_subjects
38 | @subjects ||= YAML::load File.open(File.expand_path('../subjects.yml', File.dirname(__FILE__) ) )
39 | end
40 |
41 | end
--------------------------------------------------------------------------------
/classic.rb:
--------------------------------------------------------------------------------
1 | require_relative 'lib/load_path'
2 | require 'post'
3 | require 'db_connection'
4 | require 'sinatra'
5 | require 'padrino-helpers'
6 | require 'rack-flash'
7 |
8 | enable :sessions
9 | set :per_page, 20
10 |
11 | Sinatra.register Padrino::Helpers
12 | use Rack::Flash
13 |
14 | get "/" do
15 | page = params[:page].try(:to_i) || 1
16 | @posts = Post.visible.order_by([:date, :desc]).skip( (page-1)*20 ).limit(settings.per_page)
17 | @posts.instance_eval do
18 | def num_of_page
19 | (Post.visible.length / settings.per_page.to_f).ceil.to_i
20 | end
21 | end
22 | erb :index
23 | end
24 |
25 | post "/mark_hide/:id" do
26 | @post = Post.find(params[:id])
27 | @post.update_attributes(:visible => false)
28 | flash[:notice] = "Successfully hide post #{@post.subject}. id #{@post.id}"
29 | redirect "/?page=#{params[:page]}"
30 | end
31 |
32 | post "/update/:id" do
33 | @post = Post.find(params[:id])
34 | puts params
35 | puts params["post"]
36 | @post.attributes = params["post"]
37 | if @post.save
38 | flash[:notice] = "Successfully updated post #{@post.subject}. id #{@post.id}"
39 | else
40 | flash[:alert] = "Failed to save #{@post.subject}. id #{@post.id}"
41 | end
42 | redirect "/?page=#{params[:page]}"
43 | end
--------------------------------------------------------------------------------
/spec/aggregator_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'aggregator'
3 |
4 | describe Aggregator do
5 |
6 | describe "sync" do
7 |
8 | before(:each) do
9 | @aggregator = Aggregator.new
10 | @aggregator.stub(:get_subjects).and_return{ ["subject1", "subject2"] }
11 | end
12 |
13 | it "should save a mail with a message" do
14 | Mail.stub(:find).and_yield( mail("subject1") )
15 | @aggregator.sync
16 | Email.all.count.should == 1
17 | Post.all.count == 1
18 | end
19 |
20 | it "should not save mail with bad subject" do
21 | Mail.stub(:find).and_yield( mail("bad subject") )
22 | @aggregator.sync
23 | Email.all.count.should == 0
24 | Post.all.count == 0
25 | end
26 |
27 | it "should save two mail with multiple messages" do
28 | Mail.stub(:find).and_yield( mail("subject1", 3) ).and_yield( mail("subject2", 2) )
29 | @aggregator.sync
30 | Email.all.count.should == 2
31 | Post.all.count == 5
32 | end
33 |
34 | it "should not save two mail with same message id" do
35 | Mail.stub(:find).and_yield( mail("subject1", 3, '123') ).and_yield( mail("subject2", 2, '123') )
36 | @aggregator.sync
37 | Email.all.count.should == 1
38 | Post.all.count == 3
39 | end
40 |
41 | def mail(sub, times = 1, msg_id = nil)
42 | m = Mail.new do
43 | subject sub
44 | message_id msg_id || rand.to_s
45 | text_part do
46 | body mail_body * times
47 | end
48 | end
49 | m.date = Date.today
50 | m
51 | end
52 |
53 | end
54 |
55 | end
--------------------------------------------------------------------------------
/spec/post_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'post'
3 |
4 | POST_STATUS = ['new', 'open', 'closed', 'awaiting']
5 |
6 | describe Post do
7 |
8 | it "should be created in the database" do
9 | Post.create(:message_id => "xxx@yyy.com",
10 | :date => Date.today,
11 | :subject => "this is a subject",
12 | :body => "this is a body")
13 | Post.all.count.should == 1
14 | Post.first.message_id.should == "xxx@yyy.com"
15 | Post.first.date.should == Date.today
16 | Post.first.subject.should == "this is a subject"
17 | Post.first.body.should == "this is a body"
18 | Post.first.status.should == "new"
19 | Post.first.visible.should == true
20 | end
21 |
22 | it "should hide a post" do
23 | post = Fabricate(:post, :visible => true)
24 | post.visible.should == true
25 | post.hide
26 | post.reload
27 | post.visible.should == false
28 | end
29 |
30 | it "should show a post" do
31 | post = Fabricate(:post, :visible => false)
32 | post.visible.should == false
33 | post.show
34 | post.reload
35 | post.visible.should == true
36 | end
37 |
38 | it "should list visible posts" do
39 | post = Fabricate(:post, :visible => true)
40 | Post.list.count.should == 1
41 | end
42 |
43 | it "should not list invisible posts" do
44 | post = Fabricate(:post, :visible => false)
45 | Post.list.count.should == 0
46 | end
47 |
48 | it "should change post status" do
49 | post = Fabricate(:post, :status => POST_STATUS[0] )
50 | post.change_status(POST_STATUS[1])
51 | post.reload
52 | post.status.should == POST_STATUS[1]
53 | end
54 |
55 | end
--------------------------------------------------------------------------------
/spec/parser_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'parser'
3 |
4 | describe Parser do
5 |
6 | before(:each) do
7 | @class = Class.new do
8 | extend Parser
9 | end
10 | end
11 |
12 | it "can parse mail with 0 post" do
13 | msg = %{
14 | // xxx xxx\n// xxxx\n\nhttp://xxx.com/xxx\nloremLorem ipsum\n deserunt mollit anim \nid est laborum.\n\nCreate an Account: \nhttps://www.feedmyinbox.com/account/create/194661//?utm_source=fmi&utm_medium=email&utm_campaign=feed-email\n\nUnsubscribe here: \nhttp://www.feedmyinbox.com/feeds/unsubscribe/?utm_source=fmi&utm_medium=email&utm_campaign=feed-email\n\n--\nThis email was carefully delivered by FeedMyInbox.com. \nPO Box 682532 Franklin, TN 37068
15 | }
16 | @class.parse(mail(msg)) do |post_array|
17 | post_array.length.should == 3
18 | post_array[0].should == "xxx xxx"
19 | post_array[1].should == Date.today
20 | post_array[2].should match /deserunt mollit anim id est laborum/
21 | end
22 | end
23 |
24 | it "can yield multiple times for mails with multiple posts" do
25 | msg = "// yyy (xxx)\n// June 13, 2011 at 3:18 PM \n\n Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n\n\n\n// ccc\n// June 13, 2011 at 3:18 PM\n\n Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n Create an Account: \nhttps://www.feedmyinbox.com/account/create/5992/?utm_source=fmi&utm_medium=email&utm_campaign=feed-email\n\nUnsubscribe here: \nhttp://www.feedmyinbox.com/feeds/unsubscribe/6ccd7/?utm_source=fmi&utm_medium=email&utm_campaign=feed-email\n\n--\nThis email was carefully delivered by FeedMyInbox.com. \nPO Box 682532 Franklin, TN 37068"
26 | count = 0
27 | @class.parse(mail(msg)) do |post_array|
28 | count += 1
29 | post_array.length.should == 3
30 | end
31 | count.should == 2
32 | end
33 |
34 | def mail(msg)
35 | m = Mail.new do
36 | text_part do
37 | body msg
38 | end
39 | end
40 | m.date = Date.today
41 | m
42 | end
43 | end
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/mongoid/mongoid.git
3 | revision: e92e42f3c8d35a29736372fed83e5ae60fedbb73
4 | specs:
5 | mongoid (2.1.0)
6 | activemodel (~> 3.0)
7 | mongo (~> 1.3)
8 | tzinfo (~> 0.3.22)
9 |
10 | GEM
11 | remote: http://rubygems.org/
12 | specs:
13 | ZenTest (4.5.0)
14 | activemodel (3.0.9)
15 | activesupport (= 3.0.9)
16 | builder (~> 2.1.2)
17 | i18n (~> 0.5.0)
18 | activesupport (3.0.9)
19 | archive-tar-minitar (0.5.2)
20 | autotest (4.4.6)
21 | ZenTest (>= 4.4.1)
22 | awesome_print (0.4.0)
23 | bson (1.3.1)
24 | bson_ext (1.3.1)
25 | builder (2.1.2)
26 | columnize (0.3.4)
27 | diff-lcs (1.1.2)
28 | fabrication (1.0.1)
29 | faker (0.9.5)
30 | i18n (~> 0.4)
31 | http_router (0.5.4)
32 | rack (>= 1.0.0)
33 | url_mount (~> 0.2.1)
34 | i18n (0.5.0)
35 | linecache19 (0.5.12)
36 | ruby_core_source (>= 0.1.4)
37 | mail (2.3.0)
38 | i18n (>= 0.4.0)
39 | mime-types (~> 1.16)
40 | treetop (~> 1.4.8)
41 | mime-types (1.16)
42 | mongo (1.3.1)
43 | bson (>= 1.3.1)
44 | padrino-core (0.9.21)
45 | activesupport (>= 3.0.0)
46 | http_router (~> 0.5.4)
47 | sinatra (>= 1.1.0)
48 | thor (>= 0.14.3)
49 | tzinfo
50 | padrino-helpers (0.9.21)
51 | i18n (>= 0.4.1)
52 | padrino-core (= 0.9.21)
53 | polyglot (0.3.1)
54 | rack (1.3.1)
55 | rack-flash (0.1.2)
56 | rack
57 | rspec (2.6.0)
58 | rspec-core (~> 2.6.0)
59 | rspec-expectations (~> 2.6.0)
60 | rspec-mocks (~> 2.6.0)
61 | rspec-core (2.6.4)
62 | rspec-expectations (2.6.0)
63 | diff-lcs (~> 1.1.2)
64 | rspec-mocks (2.6.0)
65 | ruby-debug-base19 (0.11.25)
66 | columnize (>= 0.3.1)
67 | linecache19 (>= 0.5.11)
68 | ruby_core_source (>= 0.1.4)
69 | ruby-debug19 (0.11.6)
70 | columnize (>= 0.3.1)
71 | linecache19 (>= 0.5.11)
72 | ruby-debug-base19 (>= 0.11.19)
73 | ruby_core_source (0.1.5)
74 | archive-tar-minitar (>= 0.5.2)
75 | shotgun (0.9)
76 | rack (>= 1.0)
77 | simplecov (0.4.2)
78 | simplecov-html (~> 0.4.4)
79 | simplecov-html (0.4.5)
80 | sinatra (1.3.0.e)
81 | rack (~> 1.3)
82 | tilt (~> 1.3)
83 | thor (0.14.6)
84 | tilt (1.3.2)
85 | treetop (1.4.9)
86 | polyglot (>= 0.3.1)
87 | tzinfo (0.3.29)
88 | url_mount (0.2.1)
89 | rack
90 |
91 | PLATFORMS
92 | ruby
93 |
94 | DEPENDENCIES
95 | autotest
96 | awesome_print
97 | bson_ext
98 | fabrication
99 | faker
100 | mail
101 | mongoid!
102 | padrino-helpers
103 | rack-flash
104 | rspec
105 | ruby-debug19
106 | shotgun
107 | simplecov
108 | sinatra (= 1.3.0e)
109 |
--------------------------------------------------------------------------------