├── .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 | --------------------------------------------------------------------------------