Permalink
Browse files

Hello Mailboxer

  • Loading branch information...
0 parents commit 282bb0b09ee54cfdafa024af7bfab4f31ad4aca3 @Roendal Roendal committed Mar 3, 2011
Showing with 11,605 additions and 0 deletions.
  1. +11 −0 .gitignore
  2. +19 −0 Gemfile
  3. +139 −0 Gemfile.lock
  4. +20 −0 LICENSE.txt
  5. +3 −0 README.rdoc
  6. +50 −0 Rakefile
  7. +1 −0 VERSION
  8. +56 −0 app/models/mailboxer_conversation.rb
  9. +20 −0 app/models/mailboxer_mail.rb
  10. +316 −0 app/models/mailboxer_mailbox.rb
  11. +38 −0 app/models/mailboxer_message.rb
  12. +15 −0 lib/generators/mailboxer/install_generator.rb
  13. +1 −0 lib/generators/mailboxer/templates/INSTALL
  14. +37 −0 lib/generators/mailboxer/templates/migration.rb
  15. +8 −0 lib/mailboxer.rb
  16. +10 −0 lib/mailboxer/engine.rb
  17. +200 −0 lib/mailboxer/models/messageable.rb
  18. +62 −0 mailboxer.gemspec
  19. +17 −0 spec/dummy/.project
  20. +5 −0 spec/dummy/Gemfile
  21. +136 −0 spec/dummy/Gemfile.lock
  22. +7 −0 spec/dummy/Rakefile
  23. +3 −0 spec/dummy/app/controllers/application_controller.rb
  24. +98 −0 spec/dummy/app/controllers/users_controller.rb
  25. +2 −0 spec/dummy/app/helpers/application_helper.rb
  26. +2 −0 spec/dummy/app/helpers/users_helper.rb
  27. +3 −0 spec/dummy/app/models/user.rb
  28. +14 −0 spec/dummy/app/views/layouts/application.html.erb
  29. +10 −0 spec/dummy/app/views/users/.tmp_prueba.html.erb.99070~
  30. +15 −0 spec/dummy/app/views/users/.tmp_show.html.erb.55603~
  31. +15 −0 spec/dummy/app/views/users/.tmp_show.html.erb.59096~
  32. +10 −0 spec/dummy/app/views/users/.tmp_show.html.erb.85273~
  33. +21 −0 spec/dummy/app/views/users/_form.html.erb
  34. +6 −0 spec/dummy/app/views/users/edit.html.erb
  35. +25 −0 spec/dummy/app/views/users/index.html.erb
  36. +5 −0 spec/dummy/app/views/users/new.html.erb
  37. 0 spec/dummy/app/views/users/prueba.html.erb
  38. +101 −0 spec/dummy/app/views/users/show.html.erb
  39. +4 −0 spec/dummy/config.ru
  40. +45 −0 spec/dummy/config/application.rb
  41. +10 −0 spec/dummy/config/boot.rb
  42. +22 −0 spec/dummy/config/database.yml
  43. +5 −0 spec/dummy/config/environment.rb
  44. +26 −0 spec/dummy/config/environments/development.rb
  45. +49 −0 spec/dummy/config/environments/production.rb
  46. +35 −0 spec/dummy/config/environments/test.rb
  47. +7 −0 spec/dummy/config/initializers/backtrace_silencers.rb
  48. +10 −0 spec/dummy/config/initializers/inflections.rb
  49. +5 −0 spec/dummy/config/initializers/mime_types.rb
  50. +7 −0 spec/dummy/config/initializers/secret_token.rb
  51. +8 −0 spec/dummy/config/initializers/session_store.rb
  52. +5 −0 spec/dummy/config/locales/en.yml
  53. +60 −0 spec/dummy/config/routes.rb
  54. +13 −0 spec/dummy/db/migrate/20110228120600_create_users.rb
  55. +37 −0 spec/dummy/db/migrate/20110302132940_create_mailboxer.rb
  56. +50 −0 spec/dummy/db/schema.rb
  57. +26 −0 spec/dummy/public/404.html
  58. +26 −0 spec/dummy/public/422.html
  59. +26 −0 spec/dummy/public/500.html
  60. 0 spec/dummy/public/favicon.ico
  61. +2 −0 spec/dummy/public/javascripts/application.js
  62. +965 −0 spec/dummy/public/javascripts/controls.js
  63. +974 −0 spec/dummy/public/javascripts/dragdrop.js
  64. +1,123 −0 spec/dummy/public/javascripts/effects.js
  65. +6,001 −0 spec/dummy/public/javascripts/prototype.js
  66. +191 −0 spec/dummy/public/javascripts/rails.js
  67. 0 spec/dummy/public/stylesheets/.gitkeep
  68. +56 −0 spec/dummy/public/stylesheets/scaffold.css
  69. +6 −0 spec/dummy/script/rails
  70. +125 −0 spec/dummy/spec/controllers/users_controller_spec.rb
  71. +15 −0 spec/dummy/spec/helpers/users_helper_spec.rb
  72. +5 −0 spec/dummy/spec/models/user_spec.rb
  73. +11 −0 spec/dummy/spec/requests/users_spec.rb
  74. +35 −0 spec/dummy/spec/routing/users_routing_spec.rb
  75. +18 −0 spec/dummy/spec/views/users/edit.html.erb_spec.rb
  76. +20 −0 spec/dummy/spec/views/users/index.html.erb_spec.rb
  77. +18 −0 spec/dummy/spec/views/users/new.html.erb_spec.rb
  78. +15 −0 spec/dummy/spec/views/users/show.html.erb_spec.rb
  79. +9 −0 spec/integration/navigation_spec.rb
  80. +7 −0 spec/mailboxer_spec.rb
  81. +32 −0 spec/spec_helper.rb
11 .gitignore
@@ -0,0 +1,11 @@
+.bundle/
+log/*.log
+pkg/
+spec/dummy/db/*.sqlite3
+spec/dummy/log/*.log
+spec/dummy/tmp/
+**.tmp_*
+Gemfile.lock
+.idea
+.project
+.document
19 Gemfile
@@ -0,0 +1,19 @@
+source "http://rubygems.org"
+
+ gem "rails", "3.0.0"
+ gem "capybara", ">= 0.3.9"
+ gem "webrat"
+ gem "sqlite3-ruby", :require => "sqlite3"
+
+ if RUBY_VERSION < '1.9'
+ gem "ruby-debug", ">= 0.10.3"
+ end
+
+
+ gem "rspec-rails", ">= 2.0.0.beta"
+
+ group :development do
+ gem "bundler", "~> 1.0.0"
+ gem "jeweler", "~> 1.5.0.pre3"
+ gem "rcov", ">= 0"
+ end
139 Gemfile.lock
@@ -0,0 +1,139 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ abstract (1.0.0)
+ actionmailer (3.0.0)
+ actionpack (= 3.0.0)
+ mail (~> 2.2.5)
+ actionpack (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ builder (~> 2.1.2)
+ erubis (~> 2.6.6)
+ i18n (~> 0.4.1)
+ rack (~> 1.2.1)
+ rack-mount (~> 0.6.12)
+ rack-test (~> 0.5.4)
+ tzinfo (~> 0.3.23)
+ activemodel (3.0.0)
+ activesupport (= 3.0.0)
+ builder (~> 2.1.2)
+ i18n (~> 0.4.1)
+ activerecord (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ arel (~> 1.0.0)
+ tzinfo (~> 0.3.23)
+ activeresource (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ activesupport (3.0.0)
+ arel (1.0.1)
+ activesupport (~> 3.0.0)
+ builder (2.1.2)
+ capybara (0.4.1.2)
+ celerity (>= 0.7.9)
+ culerity (>= 0.2.4)
+ mime-types (>= 1.16)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ selenium-webdriver (>= 0.0.27)
+ xpath (~> 0.1.3)
+ celerity (0.8.8)
+ childprocess (0.1.7)
+ ffi (~> 0.6.3)
+ columnize (0.3.2)
+ culerity (0.2.15)
+ diff-lcs (1.1.2)
+ erubis (2.6.6)
+ abstract (>= 1.0.0)
+ ffi (0.6.3)
+ rake (>= 0.8.7)
+ git (1.2.5)
+ i18n (0.4.2)
+ jeweler (1.5.2)
+ bundler (~> 1.0.0)
+ git (>= 1.2.5)
+ rake
+ json_pure (1.5.1)
+ linecache (0.43)
+ mail (2.2.15)
+ activesupport (>= 2.3.6)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ nokogiri (1.4.4)
+ polyglot (0.3.1)
+ rack (1.2.1)
+ rack-mount (0.6.13)
+ rack (>= 1.0.0)
+ rack-test (0.5.7)
+ rack (>= 1.0)
+ rails (3.0.0)
+ actionmailer (= 3.0.0)
+ actionpack (= 3.0.0)
+ activerecord (= 3.0.0)
+ activeresource (= 3.0.0)
+ activesupport (= 3.0.0)
+ bundler (~> 1.0.0)
+ railties (= 3.0.0)
+ railties (3.0.0)
+ actionpack (= 3.0.0)
+ activesupport (= 3.0.0)
+ rake (>= 0.8.4)
+ thor (~> 0.14.0)
+ rake (0.8.7)
+ rcov (0.9.9)
+ rspec (2.5.0)
+ rspec-core (~> 2.5.0)
+ rspec-expectations (~> 2.5.0)
+ rspec-mocks (~> 2.5.0)
+ rspec-core (2.5.1)
+ rspec-expectations (2.5.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.5.0)
+ rspec-rails (2.5.0)
+ actionpack (~> 3.0)
+ activesupport (~> 3.0)
+ railties (~> 3.0)
+ rspec (~> 2.5.0)
+ ruby-debug (0.10.4)
+ columnize (>= 0.1)
+ ruby-debug-base (~> 0.10.4.0)
+ ruby-debug-base (0.10.4)
+ linecache (>= 0.3)
+ rubyzip (0.9.4)
+ selenium-webdriver (0.1.3)
+ childprocess (~> 0.1.5)
+ ffi (~> 0.6.3)
+ json_pure
+ rubyzip
+ sqlite3 (1.3.3)
+ sqlite3-ruby (1.3.3)
+ sqlite3 (>= 1.3.3)
+ thor (0.14.6)
+ treetop (1.4.9)
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.24)
+ webrat (0.7.3)
+ nokogiri (>= 1.2.0)
+ rack (>= 1.0)
+ rack-test (>= 0.5.3)
+ xpath (0.1.3)
+ nokogiri (~> 1.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bundler (~> 1.0.0)
+ capybara (>= 0.3.9)
+ jeweler (~> 1.5.0.pre3)
+ rails (= 3.0.0)
+ rcov
+ rspec-rails (>= 2.0.0.beta)
+ ruby-debug (>= 0.10.3)
+ sqlite3-ruby
+ webrat
20 LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Eduardo Casanova Cuesta
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3 README.rdoc
@@ -0,0 +1,3 @@
+= Mailboxer
+
+This project rocks and uses MIT-LICENSE.
50 Rakefile
@@ -0,0 +1,50 @@
+require 'rubygems'
+require 'bundler'
+begin
+ Bundler.setup(:default, :development)
+rescue Bundler::BundlerError => e
+ $stderr.puts e.message
+ $stderr.puts "Run `bundle install` to install missing gems"
+ exit e.status_code
+end
+require 'rake'
+
+require 'jeweler'
+Jeweler::Tasks.new do |gem|
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
+ gem.name = "mailboxer"
+ gem.homepage = "http://github.com/roendal/mailboxer"
+ gem.license = "MIT"
+ gem.summary = %Q{Private messaging system for rails apps.}
+ gem.description = %Q{TODO: longer description of your gem}
+ gem.email = "ecasanovac@gmail.com"
+ gem.authors = ["Eduardo Casanova Cuesta"]
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
+end
+Jeweler::RubygemsDotOrgTasks.new
+
+require 'rspec/core'
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new(:spec) do |spec|
+ spec.pattern = FileList['spec/**/*_spec.rb']
+end
+
+RSpec::Core::RakeTask.new(:rcov) do |spec|
+ spec.pattern = 'spec/**/*_spec.rb'
+ spec.rcov = true
+end
+
+task :default => :spec
+
+require 'rake/rdoctask'
+Rake::RDocTask.new do |rdoc|
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
+
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = "mailboxer #{version}"
+ rdoc.rdoc_files.include('README*')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
1 VERSION
@@ -0,0 +1 @@
+0.0.0
56 app/models/mailboxer_conversation.rb
@@ -0,0 +1,56 @@
+class MailboxerConversation < ActiveRecord::Base
+ attr_reader :originator, :original_message, :last_sender, :last_message, :users
+ has_many :mailboxer_messages
+ #has_many :mailboxer_mails
+ before_create :clean
+ #looks like shit but isnt too bad
+ #has_many :users, :through :messages, :source => :recipients, :uniq => true doesnt work due to recipients being a habtm association
+ has_many :recipients, :class_name => 'User', :finder_sql =>
+ 'SELECT users.* FROM mailboxer_conversations
+ INNER JOIN mailboxer_messages ON mailboxer_conversations.id = mailboxer_messages.mailboxer_conversation_id
+ INNER JOIN mailboxer__recipients ON mailboxer__recipients.message_id = mailboxer_messages.id
+ INNER JOIN users ON messages_recipients.recipient_id = users.id
+ WHERE conversations.id = #{self.id} GROUP BY users.id;'
+
+ #originator of the conversation.
+ def originator()
+ @orignator = self.original_message.sender if @originator.nil?
+ return @orignator
+ end
+
+ #first message of the conversation.
+ def original_message()
+ @original_message = self.mailboxer_messages.find(:first, :order => 'created_at') if @original_message.nil?
+ return @original_message
+ end
+
+ #sender of the last message.
+ def last_sender()
+ @last_sender = self.last_message.sender if @last_sender.nil?
+ return @last_sender
+ end
+
+ #last message in the conversation.
+ def last_message()
+ @last_message = self.mailboxer_messages.find(:first, :order => 'created_at DESC') if @last_message.nil?
+ return @last_message
+ end
+
+ #all users involved in the conversation.
+ def users()
+ if(@users.nil?)
+ @users = self.mailboxer_recipients.clone
+ @users << self.originator unless @users.include?(self.originator)
+ end
+ return @users
+ end
+
+ protected
+ #[empty method]
+ #
+ #this gets called before_create. Implement this if you wish to clean out illegal content such as scripts or anything that will break layout. This is left empty because what is considered illegal content varies.
+ def clean()
+ return if(subject.nil?)
+ #strip all illegal content here. (scripts, shit that will break layout, etc.)
+ end
+end
20 app/models/mailboxer_mail.rb
@@ -0,0 +1,20 @@
+class MailboxerMail < ActiveRecord::Base
+ belongs_to :mailboxer_message
+ belongs_to :user
+ #belongs_to :mailboxer_conversation
+
+ #sets the read attribute of the mail message to true.
+ def mark_as_read()
+ update_attribute('read', true)
+ end
+
+ #sets the read attribute of the mail message to false.
+ def mark_as_unread()
+ update_attribute('read', false)
+ end
+
+ def mailboxer_conversation
+ return self.mailboxer_message.mailboxer_conversation
+ end
+
+end
316 app/models/mailboxer_mailbox.rb
@@ -0,0 +1,316 @@
+class MailboxerMailbox
+ #this is used to filter mail by mailbox type, use the [] method rather than setting this directly.
+ attr_accessor :type
+ #the user/owner of this mailbox, set when initialized.
+ attr_reader :user
+ #creates a new Mailbox instance with the given user and optional type.
+ def initialize(user, type = :all)
+ @user = user
+ @type = type
+ end
+ #sets the mailbox type to the symbol corresponding to the given val.
+ def type=(val)
+ @type = val.to_sym
+ end
+ #returns a count of mail messages filtered by type and filter, if set.
+ #
+ #*this performs an actual sql count rather than selecting all mail and then gettin a length on the array... not a big deal but this could be something that is checked often to notify the user when they receive a new mail.
+ #
+ #====params:
+ #filter:: filters the count by the 'read' attribute.
+ #* :all - count of both read and unread mail.
+ #* :read - count of read mail.
+ #* :unread - count of unread mail.
+ #options:: see mail for acceptable options.
+ #
+ #====returns:
+ #number of mail messages
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #get number of unread mail messages in phil's inbox
+ # phil.mailbox[:inbox].mail_count(:unread)
+ #
+ def mail_count(filter = :all, options = {})
+ default_options = {:conditions => ["user_id = ?", @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ add_conditions!(default_options, "read = ?", filter == :read) unless filter == :all
+ return count_mail(default_options, options)
+ end
+ #returns an array of all Mail for the user filtered by type, if set.
+ #
+ #====params:
+ #options:: all valid find options are accepted as well as an additional conversation option.
+ #* :conversation - Conversation object to filter mail only belonging to this conversation.
+ #* :conditions - same as find conditions however the array version of conditions will not work, i.e., :conditions => ['mail.read = ?', false] will not work here.
+ #* all other find options will work as expected.
+ #
+ #====returns:
+ #array of Mail.
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #get all mail messages belonging to phil
+ # phil.mailbox.mail
+ #
+ # #get all mail messages in phil's inbox associated with conversation 23
+ # phil.mailbox[:inbox].mail(:conversation => Conversation.find(23))
+ #
+ # #get all unread mail messages belonging to phil associated with conversation 23
+ # phil.mailbox.mail(:conversation => Conversation.find(23), :conditions => 'mail.read = false')
+ #
+ def mail(options = {})
+ default_options = {:conditions => ["user_id = ?", @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ return get_mail(default_options, options)
+ end
+ #returns an array of unread Mail for the user filtered by type, if set.
+ #
+ #====params:
+ #options:: see mail for acceptable options.
+ #
+ #====returns:
+ #array of Mail.
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #get all unread mail in phil's inbox
+ # phil.mailbox[:inbox].unread_mail
+ #
+ def unread_mail(options = {})
+ default_options = {:conditions => ["read = ? AND user_id = ?", false, @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ return get_mail(default_options, options)
+ end
+ #returns an array of read Mail for the user filtered by type, if set.
+ #
+ #====params:
+ #options:: see mail for acceptable options.
+ #
+ #====returns:
+ #array of Mail.
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #get all read mail in phil's inbox
+ # phil.mailbox[:inbox].read_mail
+ #
+ def read_mail(options = {})
+ default_options = {:conditions => ["read = ? AND user_id = ?", true, @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ return get_mail(default_options, options)
+ end
+ #returns an array of the latest Mail message for each conversation the user is involved in filtered by type, if set.
+ #
+ #*possible use for this would be an inbox view of your mail so you can easily see the status of all the convos your involved in.
+ #
+ #====params:
+ #options:: see mail for acceptable options.
+ #
+ #====returns:
+ #array of Mail.
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #get a list of the latest received mail for each conversation
+ # phil.mailbox[:inbox].latest_mail
+ #
+ def latest_mail(options = {})
+ return only_latest(mail(options))
+ end
+ #adds a mail message to the user's mailbox specified by type.
+ #
+ #*this is used when sending a new message, all the recipients get a mail added to their inbox and the sender gets a mail in their sentbox.
+ #
+ #====params:
+ #msg::
+ # Message object from which a new mail is created from.
+ #
+ #====returns:
+ #new Mail.
+ #
+ def add(msg)
+ attributes = {:mailboxer_message => msg}
+ attributes[:mailbox_type] = @type.to_s unless @type == :all
+ attributes[:read] = msg.sender.id == @user.id
+ attributes[:user_id] = @user.id ##AÑADIDO POR MI
+ mail_msg = MailboxerMail.new(attributes)
+ @user.mailboxer_mails << mail_msg
+ return mail_msg
+ end
+ #marks all the mail messages matched by the options and type as read.
+ #
+ #====params:
+ #options:: see mail for acceptable options.
+ #
+ #====returns:
+ #array of Mail.
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #mark all inbox messages as read
+ # phil.mailbox[:inbox].mark_as_read()
+ #
+ def mark_as_read(options = {})
+ default_options = {:conditions => ["user_id = ?", @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ return update_mail("read = true", default_options, options)
+ end
+ #marks all the mail messages matched by the options and type as unread, except for sent messages.
+ #
+ #====params:
+ #options:: see mail for acceptable options.
+ #
+ #====returns:
+ #array of Mail.
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #mark all inbox messages as unread
+ # phil.mailbox[:inbox].mark_as_unread()
+ #
+ def mark_as_unread(options = {})
+ default_options = {:conditions => ["mailbox_type != ? AND user_id = ?",@user.mailbox_types[:sent].to_s, @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ return update_mail("read = false", default_options, options)
+ end
+ #moves all mail matched by the options to the given mailbox. sent messages stay in the sentbox.
+ #
+ #====params:
+ #mailbox:: the mailbox_type to move the mail messages to. (ex. :inbox, :trash)
+ #options:: see mail for acceptable options.
+ #
+ def move_to(mailbox, options = {})
+ mailbox = mailbox.to_sym
+ trash = mailbox == @user.mailbox_types[:deleted].to_sym
+ default_options = {:conditions => ["user_id = ?", @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ if(!trash)
+ #conditional update because sentmail is always sentmail - I believe case if the most widely supported conditional, mysql also has an if which would work as well but i think mysql is the only one to support it
+ return update_mail("trashed = 'f', mailbox_type =
+ CASE mailbox_type
+ WHEN '#{@user.mailbox_types[:sent].to_s}' THEN mailbox_type
+ ELSE '#{mailbox.to_s}'
+ END", default_options, options)
+ end
+ return update_mail("trashed = 't'", default_options, options)
+ end
+ #permanantly deletes all the mail messages matched by the options. Use move_to(:trash) instead if you want to send to user's trash without deleting.
+ #
+ #====params:
+ #options:: see mail for acceptable options.
+ #
+ def delete(options = {})
+ default_options = {:conditions => ["user_id = ?", @user.id]}
+ add_mailbox_condition!(default_options, @type)
+ return delete_mail(default_options, options)
+ end
+ #alias for add
+ def <<(msg)
+ return self.add(msg)
+ end
+ #deletes all messages that have been trashed and match the options if passed.
+ #
+ #====params:
+ #options:: see mail for acceptable options.
+ #
+ def empty_trash(options = {})
+ default_options = {:conditions => ["user_id = ? AND trashed = ?", @user.id, true]}
+ add_mailbox_condition!(default_options, @type)
+ return delete_mail(default_options, options)
+ end
+ #return true if the user is involved in the given conversation.
+ def has_conversation?(conversation)
+ return mail_count(:all, :conversation => conversation) != 0
+ end
+ #sets the mailbox type and returns itself.
+ #
+ #====params:
+ #mailbox_type::
+ # type of mailbox to filter mail by, this can be anything, but the three most likely values for this will be the received, sent, and trash values set within the acts_as_messageable method.
+ #
+ #====returns:
+ #self
+ #
+ #====example:
+ # phil = User.find(3123)
+ #
+ # #all mails in the user's inbox
+ # phil.mailbox[:inbox].mail
+ #
+ def [](mailbox_type)
+ self.type = mailbox_type
+ return self
+ end
+ private
+ def get_mail(default_options, options)
+ build_options(default_options, options) unless options.empty?
+ #return @user.mailboxer_mails.find(:all, default_options)
+ return MailboxerMail.find(:all, default_options)
+ end
+ def update_mail(updates, default_options, options)
+ build_options(default_options, options) unless options.empty?
+ #return @user.mailboxer_mails.update_all(updates, default_options[:conditions])
+ return MailboxerMail.update_all(updates, default_options[:conditions])
+ end
+ def delete_mail(default_options, options)
+ build_options(default_options, options) unless options.empty?
+ return MailboxerMail.delete_all(default_options[:conditions])
+ end
+ def count_mail(default_options, options)
+ build_options(default_options, options) unless options.empty?
+ return MailboxerMail.count(:all, default_options)
+ end
+ def build_options(default_options, options)
+ add_conversation_condition!(default_options, options[:conversation]) unless options[:conversation].nil?
+ options.delete(:conversation)
+ add_conditions!(default_options, options[:conditions]) unless options[:conditions].nil?
+ options.delete(:conditions)
+ default_options.merge!(options)
+ end
+ def only_latest(mail)
+ convos = []
+ latest = []
+ mail.each do |m|
+ next if(convos.include?(m.mailboxer_conversation_id))
+ convos << m.mailboxer_conversation_id
+ latest << m
+ end
+ return latest
+ end
+ def add_mailbox_condition!(options, mailbox_type)
+ return if mailbox_type == :all
+ return add_conditions!(options, "mailbox_type = ? AND trashed = ?", mailbox_type.to_s, false) unless mailbox_type == @user.mailbox_types[:deleted]
+ return add_conditions!(options, "trashed = ?", true)
+ end
+ def add_conversation_condition!(options, conversation)
+ options.merge!({:order => 'created_at ASC'})
+ if(conversation.is_a?(Array))
+ conversation.map! {|c| c.is_a?(Integer) ? c : c.id}
+ else
+ conversation = conversation.is_a?(Integer) ? [conversation] : [conversation.id]
+ end
+ return add_conditions!(options, "conversation_id IN (?)", conversation)
+ end
+ def add_conditions!(options, conditions, *values)
+ return nil unless options.is_a?(Hash)
+ if(options[:conditions].nil?)
+ options[:conditions] = values.length == 0 ? conditions : [conditions]
+ elsif(options[:conditions].is_a?(Array))
+ options[:conditions][0] = "(#{options[:conditions][0]}) AND (#{conditions})"
+ else
+ options[:conditions] = "(#{options[:conditions]}) AND (#{conditions})"
+ end
+ values.each do |val|
+ options[:conditions].push(val)
+ end
+ return options
+ end
+end
38 app/models/mailboxer_message.rb
@@ -0,0 +1,38 @@
+class MailboxerMessage < ActiveRecord::Base
+ #any additional info that needs to be sent in a message (ex. I use these to determine request types)
+ serialize :headers
+
+ class_inheritable_accessor :on_deliver_callback
+ protected :on_deliver_callback
+ belongs_to :sender, :class_name => 'User', :foreign_key => 'sender_id'
+ belongs_to :mailboxer_conversation
+ has_and_belongs_to_many :mailboxer_recipients, :class_name => 'User', :join_table => 'mailboxer_recipients', :association_foreign_key => 'recipient_id'
+
+ #delivers a message to the the given mailbox of all recipients, calls the on_deliver_callback if initialized.
+ #
+ #====params:
+ #mailbox_type:: the mailbox to send the message to
+ #clean:: calls the clean method if this is set (must be implemented)
+ #
+ def deliver(mailbox_type, clean = true)
+ clean() if clean
+ self.save()
+ self.mailboxer_recipients.each do |r|
+ r.mailbox[mailbox_type] << self
+ end
+ self.on_deliver_callback.call(self, mailbox_type) unless self.on_deliver_callback.nil?
+ end
+
+ #sets the on_deliver_callback to the passed method. The method call should expect 2 params (message, mailbox_type).
+ def MailboxerMessage.on_deliver(method)
+ self.on_deliver_callback = method
+ end
+
+ protected
+ #[empty method]
+ #
+ #this gets called when a message is delivered and the clean param is set (default). Implement this if you wish to clean out illegal content such as scripts or anything that will break layout. This is left empty because what is considered illegal content varies.
+ def clean()
+ #strip all illegal content here. (scripts, shit that will break layout, etc.)
+ end
+end
15 lib/generators/mailboxer/install_generator.rb
@@ -0,0 +1,15 @@
+class Mailboxer::InstallGenerator < Rails::Generators::Base #:nodoc:
+ include Rails::Generators::Migration
+
+ source_root File.expand_path('../templates', __FILE__)
+
+ require 'rails/generators/active_record'
+
+ def self.next_migration_number(dirname)
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
+ end
+
+ def create_migration_file
+ migration_template 'migration.rb', 'db/migrate/create_mailboxer.rb'
+ end
+end
1 lib/generators/mailboxer/templates/INSTALL
@@ -0,0 +1 @@
+run 'rake db:migrate' to complete install
37 lib/generators/mailboxer/templates/migration.rb
@@ -0,0 +1,37 @@
+class CreateMailboxer < ActiveRecord::Migration
+ def self.up
+ create_table :mailboxer_conversations do |t|
+ t.column :subject, :string, :default => ""
+ t.column :created_at, :datetime, :null => false
+ end
+ create_table :mailboxer_mails do |t|
+ t.column :user_id, :integer, :null => false
+ t.column :mailboxer_message_id, :integer, :null => false
+ #t.column :mailboxer_conversation_id, :integer
+ t.column :read, :boolean, :default => false
+ t.column :trashed, :boolean, :default => false
+ t.column :mailbox_type, :string, :limit => 25
+ t.column :created_at, :datetime, :null => false
+ end
+ create_table :mailboxer_messages do |t|
+ t.column :body, :text
+ t.column :subject, :string, :default => ""
+ t.column :headers, :text
+ t.column :sender_id, :integer, :null => false
+ t.column :mailboxer_conversation_id, :integer
+ t.column :sent, :boolean, :default => false
+ t.column :created_at, :datetime, :null => false
+ end
+ create_table :mailboxer_recipients, :id => false do |t|
+ t.column :mailboxer_message_id, :integer, :null => false
+ t.column :recipient_id, :integer, :null => false
+ end
+ end
+
+ def self.down
+ drop_table :mailboxer_mails
+ drop_table :mailboxer_conversations
+ drop_table :mailboxer_recipients
+ drop_table :mailboxer_messages
+ end
+end
8 lib/mailboxer.rb
@@ -0,0 +1,8 @@
+module Mailboxer
+ module Models
+ autoload :Messageable, 'mailboxer/models/messageable'
+ end
+end
+# reopen ActiveRecord and include all the above to make
+# them available to all our models if they want it
+require 'mailboxer/engine'
10 lib/mailboxer/engine.rb
@@ -0,0 +1,10 @@
+module Mailboxer
+ class Engine < Rails::Engine
+
+ initializer "mailboxer.models.messageable" do
+ ActiveSupport.on_load(:active_record) do
+ include Mailboxer::Models::Messageable
+ end
+ end
+ end
+end
200 lib/mailboxer/models/messageable.rb
@@ -0,0 +1,200 @@
+module Mailboxer
+ module Models
+ module Messageable
+ extend ActiveSupport::Concern
+
+ included do
+ has_many :mailboxer_messages
+ cattr_accessor :mailbox_types
+ has_many :mailboxer_mails, :order => 'created_at DESC', :dependent => :delete_all
+
+ end
+ # declare the class level helper methods which
+ # will load the relevant instance methods
+ # defined below when invoked
+ module ClassMethods
+ #enables a class to send and receive messages to members of the same class - currently assumes the model is of class type 'User',
+ #some modifications to the migrations and model classes will need to be made to use a model of different type.
+ #
+ #====options:
+ #* :received - the mailbox type to store received messages (defaults to :inbox)
+ #
+ #* :sent - the mailbox type to store sent messages (defaults to :sentbox)
+ #
+ #* :deleted - the mailbox type to store deleted messages (defaults to :trash)
+ #
+ #====example:
+ # acts_as_messageable :received => :in, :sent => :sent, :deleted => :garbage
+ def acts_as_messageable(options = {})
+ self.mailbox_types = {
+ :received => :inbox,
+ :sent => :sentbox,
+ :deleted => :trash
+ }.merge(options)
+ include Mailboxer::Models::Messageable::InstanceMethods
+ end
+ end
+
+ #Adds class methods
+ #module SingletonMethods
+ #end
+
+ # Adds instance methods.
+ module InstanceMethods
+ #returns an instance of class type Mailbox - this object essentially wraps the user's mail messages and provides a clean interface for accessing them.
+ #see Mailbox for more details.
+ #
+ #====example:
+ # phil = User.find(3123)
+ # phil.mailbox[:inbox].unread_mail #returns all unread mail in your inbox
+ # phil.mailbox[:sentbox].mail #returns all sent mail messages
+ #
+ def mailbox()
+ @mailbox = MailboxerMailbox.new(self) if @mailbox.nil?
+ @mailbox.type = :all
+ return @mailbox
+ end
+ #creates new Message and Conversation objects from the given parameters and delivers Mail to each of the recipients' inbox.
+ #
+ #====params:
+ #recipients::
+ # a single user object or array of users to deliver the message to.
+ #msg_body::
+ # the body of the message.
+ #subject::
+ # the subject of the message, defaults to empty string if not provided.
+ #====returns:
+ #the sent Mail.
+ #
+ #====example:
+ # phil = User.find(3123)
+ # todd = User.find(4141)
+ # phil.send_message(todd, 'whats up for tonight?', 'hey guy') #sends a Mail message to todd's inbox, and a Mail message to phil's sentbox
+ #
+ def send_message(recipients, msg_body, subject = '')
+ convo = MailboxerConversation.create({:subject => subject})
+ message = MailboxerMessage.create({:sender => self, :mailboxer_conversation => convo, :body => msg_body, :subject => subject})
+ message.mailboxer_recipients = recipients.is_a?(Array) ? mailboxer_recipients : [recipients]
+ message.deliver(self.mailbox_types[:received])
+ return mailbox[:sentbox] << message
+ end
+ #creates a new Message associated with the given conversation and delivers the reply to each of the given recipients.
+ #
+ #*explicitly calling this method is rare unless you are replying to a subset of the users involved in the conversation or
+ #if you are including someone that is not currently in the conversation.
+ #reply_to_sender, reply_to_all, and reply_to_conversation will suffice in most cases.
+ #
+ #====params:
+ #conversation::
+ # the Conversation object that the mail you are responding to belongs.
+ #recipients::
+ # a single User object or array of Users to deliver the reply message to.
+ #reply_body::
+ # the body of the reply message.
+ #subject::
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
+ #====returns:
+ #the sent Mail.
+ #
+ def reply(conversation, recipients, reply_body, subject = nil)
+ return nil if(reply_body.blank?)
+ subject = subject || "RE: #{conversation.subject}"
+ response = MailboxerMessage.create({:sender => self, :mailboxer_conversation => conversation, :body => reply_body, :subject => subject})
+ response.mailboxer_recipients = recipients.is_a?(Array) ? recipients : [recipients]
+ response.deliver(self.mailbox_types[:received])
+ return mailbox[self.mailbox_types[:sent]] << response
+ end
+ #sends a Mail to the sender of the given mail message.
+ #
+ #====params:
+ #mail::
+ # the Mail object that you are replying to.
+ #reply_body::
+ # the body of the reply message.
+ #subject::
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
+ #====returns:
+ #the sent Mail.
+ #
+ def reply_to_sender(mail, reply_body, subject = nil)
+ return reply(mail.mailboxer_conversation, mail.mailboxer_message.sender, reply_body, subject)
+ end
+ #sends a Mail to all of the recipients of the given mail message (excluding yourself).
+ #
+ #====params:
+ #mail::
+ # the Mail object that you are replying to.
+ #reply_body::
+ # the body of the reply message.
+ #subject::
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
+ #====returns:
+ #the sent Mail.
+ #
+ def reply_to_all(mail, reply_body, subject = nil)
+ msg = mail.mailboxer_message
+ recipients = msg.mailboxer_recipients.clone()
+ if(msg.sender != self)
+ recipients.delete(self)
+ if(!recipients.include?(msg.sender))
+ recipients << msg.sender
+ end
+ end
+ return reply(mail.mailboxer_conversation, recipients, reply_body, subject)
+ end
+ #sends a Mail to all users involved in the given conversation (excluding yourself).
+ #
+ #*this may have undesired effects if users have been added to the conversation after it has begun.
+ #
+ #====params:
+ #conversation::
+ # the Conversation object that the mail you are responding to belongs.
+ #reply_body::
+ # the body of the reply message.
+ #subject::
+ # the subject of the message, defaults to 'RE: [original subject]' if one isnt given.
+ #====returns:
+ #the sent Mail.
+ #
+ def reply_to_conversation(conversation, reply_body, subject = nil)
+ #move conversation to inbox if it is currently in the trash - doesnt make much sense replying to a trashed convo.
+ if((mailbox[self.mailbox_types[:deleted]].has_conversation?(conversation)))
+ mailbox.move_to(self.mailbox_types[:received], :mailboxer_conversation => conversation)
+ end
+ #remove self from recipients unless you are the originator of the convo
+ recipients = conversation.original_message.mailboxer_recipients.clone()
+ if(conversation.originator != self)
+ recipients.delete(self)
+ if(!recipients.include?(conversation.originator))
+ recipients << conversation.originator
+ end
+ end
+ return reply(conversation,recipients, reply_body, subject)
+ end
+ #returns the mail given as the parameter, marked as read.
+ def read_mail(mail)
+ return mail.mark_as_read()
+ end
+ #returns an array of the user's Mail associated with the given conversation.
+ #All mail is marked as read but the returning array is built before this so you can see which messages were unread when viewing the conversation.
+ #
+ #This returns deleted/trashed messages as well for the purpose of reading trashed convos, to disable this send the option ':conditions => "mail.trashed != true"'
+ #
+ #====params:
+ #conversation::
+ # the Conversation object that you want to read.
+ #options::
+ # any options to filter the conversation, these are used as find options so all valid options for find will work.
+ #
+ #====returns:
+ #array of Mail belonging to the given conversation.
+ #
+ def read_conversation(conversation, options = {})
+ convo_mail = self.mailbox.mail(options.merge(:mailboxer_conversation => conversation))
+ self.mailbox.mark_as_read(:mailboxer_conversation => conversation)
+ return convo_mail
+ end
+ end
+ end
+ end
+end
62 mailboxer.gemspec
@@ -0,0 +1,62 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{mailboxer}
+ s.version = "0.0.1"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Eduardo Casanova Cuesta"]
+ s.date = %q{2011-02-28}
+ s.description = %q{TODO: longer description of your gem}
+ s.email = %q{ecasanovac@gmail.com}
+ s.extra_rdoc_files = [
+ "LICENSE.txt",
+ "README.rdoc"
+ ]
+ s.homepage = %q{http://github.com/roendal/mailboxer}
+ s.licenses = ["MIT"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.7}
+ s.summary = %q{Private messaging system for rails apps.}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<rails>, ["= 3.0.0"])
+ s.add_runtime_dependency(%q<capybara>, [">= 0.3.9"])
+ s.add_runtime_dependency(%q<webrat>, [">= 0"])
+ s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 0"])
+ s.add_runtime_dependency(%q<ruby-debug>, [">= 0.10.3"])
+ s.add_runtime_dependency(%q<rspec-rails>, [">= 2.0.0.beta"])
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
+ s.add_development_dependency(%q<rcov>, [">= 0"])
+ else
+ s.add_dependency(%q<rails>, ["= 3.0.0"])
+ s.add_dependency(%q<capybara>, [">= 0.3.9"])
+ s.add_dependency(%q<webrat>, [">= 0"])
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
+ s.add_dependency(%q<ruby-debug>, [">= 0.10.3"])
+ s.add_dependency(%q<rspec-rails>, [">= 2.0.0.beta"])
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
+ s.add_dependency(%q<rcov>, [">= 0"])
+ end
+ else
+ s.add_dependency(%q<rails>, ["= 3.0.0"])
+ s.add_dependency(%q<capybara>, [">= 0.3.9"])
+ s.add_dependency(%q<webrat>, [">= 0"])
+ s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
+ s.add_dependency(%q<ruby-debug>, [">= 0.10.3"])
+ s.add_dependency(%q<rspec-rails>, [">= 2.0.0.beta"])
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
+ s.add_dependency(%q<rcov>, [">= 0"])
+ end
+end
+
17 spec/dummy/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>mailboxer - dummy</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.rubypeople.rdt.core.rubybuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.rubypeople.rdt.core.rubynature</nature>
+ </natures>
+</projectDescription>
5 spec/dummy/Gemfile
@@ -0,0 +1,5 @@
+source 'http://rubygems.org'
+
+gem 'mailboxer', :path => '../../../mailboxer'
+
+
136 spec/dummy/Gemfile.lock
@@ -0,0 +1,136 @@
+PATH
+ remote: ../../../mailboxer
+ specs:
+ mailboxer (0.0.1)
+ capybara (>= 0.3.9)
+ rails (= 3.0.0)
+ rspec-rails (>= 2.0.0.beta)
+ ruby-debug (>= 0.10.3)
+ sqlite3-ruby
+ webrat
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ abstract (1.0.0)
+ actionmailer (3.0.0)
+ actionpack (= 3.0.0)
+ mail (~> 2.2.5)
+ actionpack (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ builder (~> 2.1.2)
+ erubis (~> 2.6.6)
+ i18n (~> 0.4.1)
+ rack (~> 1.2.1)
+ rack-mount (~> 0.6.12)
+ rack-test (~> 0.5.4)
+ tzinfo (~> 0.3.23)
+ activemodel (3.0.0)
+ activesupport (= 3.0.0)
+ builder (~> 2.1.2)
+ i18n (~> 0.4.1)
+ activerecord (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ arel (~> 1.0.0)
+ tzinfo (~> 0.3.23)
+ activeresource (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ activesupport (3.0.0)
+ arel (1.0.1)
+ activesupport (~> 3.0.0)
+ builder (2.1.2)
+ capybara (0.4.1.2)
+ celerity (>= 0.7.9)
+ culerity (>= 0.2.4)
+ mime-types (>= 1.16)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ selenium-webdriver (>= 0.0.27)
+ xpath (~> 0.1.3)
+ celerity (0.8.8)
+ childprocess (0.1.7)
+ ffi (~> 0.6.3)
+ columnize (0.3.2)
+ culerity (0.2.15)
+ diff-lcs (1.1.2)
+ erubis (2.6.6)
+ abstract (>= 1.0.0)
+ ffi (0.6.3)
+ rake (>= 0.8.7)
+ i18n (0.4.2)
+ json_pure (1.5.1)
+ linecache (0.43)
+ mail (2.2.15)
+ activesupport (>= 2.3.6)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ nokogiri (1.4.4)
+ polyglot (0.3.1)
+ rack (1.2.1)
+ rack-mount (0.6.13)
+ rack (>= 1.0.0)
+ rack-test (0.5.7)
+ rack (>= 1.0)
+ rails (3.0.0)
+ actionmailer (= 3.0.0)
+ actionpack (= 3.0.0)
+ activerecord (= 3.0.0)
+ activeresource (= 3.0.0)
+ activesupport (= 3.0.0)
+ bundler (~> 1.0.0)
+ railties (= 3.0.0)
+ railties (3.0.0)
+ actionpack (= 3.0.0)
+ activesupport (= 3.0.0)
+ rake (>= 0.8.4)
+ thor (~> 0.14.0)
+ rake (0.8.7)
+ rspec (2.5.0)
+ rspec-core (~> 2.5.0)
+ rspec-expectations (~> 2.5.0)
+ rspec-mocks (~> 2.5.0)
+ rspec-core (2.5.1)
+ rspec-expectations (2.5.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.5.0)
+ rspec-rails (2.5.0)
+ actionpack (~> 3.0)
+ activesupport (~> 3.0)
+ railties (~> 3.0)
+ rspec (~> 2.5.0)
+ ruby-debug (0.10.4)
+ columnize (>= 0.1)
+ ruby-debug-base (~> 0.10.4.0)
+ ruby-debug-base (0.10.4)
+ linecache (>= 0.3)
+ rubyzip (0.9.4)
+ selenium-webdriver (0.1.3)
+ childprocess (~> 0.1.5)
+ ffi (~> 0.6.3)
+ json_pure
+ rubyzip
+ sqlite3 (1.3.3)
+ sqlite3-ruby (1.3.3)
+ sqlite3 (>= 1.3.3)
+ thor (0.14.6)
+ treetop (1.4.9)
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.24)
+ webrat (0.7.3)
+ nokogiri (>= 1.2.0)
+ rack (>= 1.0)
+ rack-test (>= 0.5.3)
+ xpath (0.1.3)
+ nokogiri (~> 1.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ mailboxer!
7 spec/dummy/Rakefile
@@ -0,0 +1,7 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+require 'rake'
+
+Dummy::Application.load_tasks
3 spec/dummy/app/controllers/application_controller.rb
@@ -0,0 +1,3 @@
+class ApplicationController < ActionController::Base
+ protect_from_forgery
+end
98 spec/dummy/app/controllers/users_controller.rb
@@ -0,0 +1,98 @@
+class UsersController < ApplicationController
+ # GET /users
+ # GET /users.xml
+ def index
+ @users = User.all
+
+ respond_to do |format|
+ format.html # index.html.erb
+ format.xml { render :xml => @users }
+ end
+
+
+ end
+
+ # GET /users/1
+ # GET /users/1.xml
+ def show
+ @pepe = User.find_by_id(1)
+ @juan = User.find_by_id(2)
+
+ if @pepe.nil?
+ @pepe = User.create(:name => "pepe")
+ end
+ if @juan.nil?
+ @juan = User.create(:name => "juan")
+ end
+
+ mensaje0 = @pepe.send_message(@juan, "Pepe le dice a Juan que mola", "Prueba de mensajes")
+ mensaje1 = @juan.reply_to_all(mensaje0, "Juan contesta a Pepe que gracias")
+ mensaje2 = @juan.reply_to_all(mensaje1, "Juan vuelve a contestar a Pepe que el tambien mola")
+ mensaje3 = @pepe.reply_to_all(mensaje2, "Pepe cocha los cinco con Juan")
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.xml { render :xml => @user }
+ end
+ end
+
+ # GET /users/new
+ # GET /users/new.xml
+ def new
+ @user = User.new
+
+ respond_to do |format|
+ format.html # new.html.erb
+ format.xml { render :xml => @user }
+ end
+ end
+
+ # GET /users/1/edit
+ def edit
+ @user = User.find(params[:id])
+ end
+
+ # POST /users
+ # POST /users.xml
+ def create
+ @user = User.new(params[:user])
+
+ respond_to do |format|
+ if @user.save
+ format.html { redirect_to(@user, :notice => 'User was successfully created.') }
+ format.xml { render :xml => @user, :status => :created, :location => @user }
+ else
+ format.html { render :action => "new" }
+ format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /users/1
+ # PUT /users/1.xml
+ def update
+ @user = User.find(params[:id])
+
+ respond_to do |format|
+ if @user.update_attributes(params[:user])
+ format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
+ format.xml { head :ok }
+ else
+ format.html { render :action => "edit" }
+ format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /users/1
+ # DELETE /users/1.xml
+ def destroy
+ @user = User.find(params[:id])
+ @user.destroy
+
+ respond_to do |format|
+ format.html { redirect_to(users_url) }
+ format.xml { head :ok }
+ end
+ end
+end
2 spec/dummy/app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
2 spec/dummy/app/helpers/users_helper.rb
@@ -0,0 +1,2 @@
+module UsersHelper
+end
3 spec/dummy/app/models/user.rb
@@ -0,0 +1,3 @@
+class User < ActiveRecord::Base
+ acts_as_messageable
+end
14 spec/dummy/app/views/layouts/application.html.erb
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Dummy</title>
+ <%= stylesheet_link_tag :all %>
+ <%= javascript_include_tag :defaults %>
+ <%= csrf_meta_tag %>
+</head>
+<body>
+
+<%= yield %>
+
+</body>
+</html>
10 spec/dummy/app/views/users/.tmp_prueba.html.erb.99070~
@@ -0,0 +1,10 @@
+<p id="notice"><%= notice %></p>
+
+<p>
+ <b>Name:</b>
+ <%= @user.name %>
+</p>
+
+
+<%= link_to 'Edit', edit_user_path(@user) %> |
+<%= link_to 'Back', users_path %>
15 spec/dummy/app/views/users/.tmp_show.html.erb.55603~
@@ -0,0 +1,15 @@
+<p id="notice"><%= notice %></p>
+
+<p>
+ <b>Name:</b>
+ <%= @user.name %>
+</p>
+
+
+<%= link_to 'Edit', edit_user_path(@user) %> |
+<%= link_to 'Back', users_path %>
+
+Pepe ------------
+<%= @pepe.mailbox[:inbox].unread_mail %>
+Juan ------------
+<%= @juan.mailbox[:inbox].unread_mail %>
15 spec/dummy/app/views/users/.tmp_show.html.erb.59096~
@@ -0,0 +1,15 @@
+<p id="notice"><%= notice %></p>
+
+<p>
+ <b>Name:</b>
+ <%= @user.name %>
+</p>
+
+
+<%= link_to 'Edit', edit_user_path(@user) %> |
+<%= link_to 'Back', users_path %>
+
+Pepe ------------
+<%= @pepe.mailbox[:inbox].unread_mail %>
+Juan ------------
+<%= @juan.mailbox[:inbox].unread_mail %>
10 spec/dummy/app/views/users/.tmp_show.html.erb.85273~
@@ -0,0 +1,10 @@
+<p id="notice"><%= notice %></p>
+
+<p>
+ <b>Name:</b>
+ <%= @user.name %>
+</p>
+
+
+<%= link_to 'Edit', edit_user_path(@user) %> |
+<%= link_to 'Back', users_path %>
21 spec/dummy/app/views/users/_form.html.erb
@@ -0,0 +1,21 @@
+<%= form_for(@user) do |f| %>
+ <% if @user.errors.any? %>
+ <div id="error_explanation">
+ <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
+
+ <ul>
+ <% @user.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <div class="field">
+ <%= f.label :name %><br />
+ <%= f.text_field :name %>
+ </div>
+ <div class="actions">
+ <%= f.submit %>
+ </div>
+<% end %>
6 spec/dummy/app/views/users/edit.html.erb
@@ -0,0 +1,6 @@
+<h1>Editing user</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Show', @user %> |
+<%= link_to 'Back', users_path %>
25 spec/dummy/app/views/users/index.html.erb
@@ -0,0 +1,25 @@
+<h1>Listing users</h1>
+
+<table>
+ <tr>
+ <th>Id</th>
+ <th>Name</th>
+ <th></th>
+ <th></th>
+ <th></th>
+ </tr>
+
+<% @users.each do |user| %>
+ <tr>
+ <td><%= user.id %></td>
+ <td><%= user.name %></td>
+ <td><%= link_to 'Show', user %></td>
+ <td><%= link_to 'Edit', edit_user_path(user) %></td>
+ <td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>
+ </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New User', new_user_path %>
5 spec/dummy/app/views/users/new.html.erb
@@ -0,0 +1,5 @@
+<h1>New user</h1>
+
+<%= render 'form' %>
+
+<%= link_to 'Back', users_path %>
0 spec/dummy/app/views/users/prueba.html.erb
No changes.
101 spec/dummy/app/views/users/show.html.erb
@@ -0,0 +1,101 @@
+<p id="notice"><%= notice %></p>
+
+
+
+<%= link_to 'Edit', edit_user_path(@user) %> |
+<%= link_to 'Back', users_path %>
+<br><br>
+<hr>
+<h1>Pepe</h1>
+<hr>
+<% user = @pepe %>
+INBOX<hr>
+<% mails = user.mailbox[:inbox].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+SENTBOX<hr>
+<% mails = user.mailbox[:sentbox].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+TRASH (vacio? <%= user.mailbox[:trash].mail.empty? %>)<hr>
+<% mails = user.mailbox[:trash].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+ALL<hr>
+<% mails = user.mailbox[:all].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+
+<br>
+
+
+<hr>
+<h1>Juan</h1>
+<hr>
+<% user = @juan %>
+INBOX<hr>
+<% mails = user.mailbox[:inbox].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+SENTBOX<hr>
+<% mails = user.mailbox[:sentbox].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+TRASH (vacio? <%= user.mailbox[:trash].mail.empty? %>)<hr>
+<% mails = user.mailbox[:trash].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+ALL<hr>
+<% mails = user.mailbox[:all].mail %>
+<% mails.each do |mail| %>
+ <b><%= mail.mailboxer_conversation.subject %></b><br>
+ TO: (<%= mail.user.name %>) <br>
+ FROM: (<%= mail.mailboxer_message.sender.name %>)<br>
+ SUBJECT: <%= mail.mailboxer_message.subject %> <br>
+ BODY: <%= mail.mailboxer_message.body %> <br>
+<% end %>
+<br>
+
+<br>
4 spec/dummy/config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run Dummy::Application
45 spec/dummy/config/application.rb
@@ -0,0 +1,45 @@
+require File.expand_path('../boot', __FILE__)
+
+require "active_model/railtie"
+require "active_record/railtie"
+require "action_controller/railtie"
+require "action_view/railtie"
+require "action_mailer/railtie"
+
+Bundler.require
+require "mailboxer"
+
+module Dummy
+ class Application < Rails::Application
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+
+ # Custom directories with classes and modules you want to be autoloadable.
+ # config.autoload_paths += %W(#{config.root}/extras)
+
+ # Only load the plugins named here, in the order given (default is alphabetical).
+ # :all can be used as a placeholder for all plugins not explicitly named.
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+
+ # Activate observers that should always be running.
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+ # config.i18n.default_locale = :de
+
+ # JavaScript files you want as :defaults (application.js is always included).
+ # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
+
+ # Configure the default encoding used in templates for Ruby 1.9.
+ config.encoding = "utf-8"
+
+ # Configure sensitive parameters which will be filtered from the log file.
+ config.filter_parameters += [:password]
+ end
+end
10 spec/dummy/config/boot.rb
@@ -0,0 +1,10 @@
+require 'rubygems'
+gemfile = File.expand_path('../../../../Gemfile', __FILE__)
+
+if File.exist?(gemfile)
+ ENV['BUNDLE_GEMFILE'] = gemfile
+ require 'bundler'
+ Bundler.setup
+end
+
+$:.unshift File.expand_path('../../../../lib', __FILE__)
22 spec/dummy/config/database.yml
@@ -0,0 +1,22 @@
+# SQLite version 3.x
+# gem install sqlite3
+development:
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ database: db/test.sqlite3
+ pool: 5
+ timeout: 5000
+
+production:
+ adapter: sqlite3
+ database: db/production.sqlite3
+ pool: 5
+ timeout: 5000
5 spec/dummy/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the rails application
+require File.expand_path('../application', __FILE__)
+
+# Initialize the rails application
+Dummy::Application.initialize!
26 spec/dummy/config/environments/development.rb
@@ -0,0 +1,26 @@
+Dummy::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the webserver when you make code changes.
+ config.cache_classes = false
+
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_view.debug_rjs = true
+ config.action_controller.perform_caching = false
+
+ # Don't care if the mailer can't send
+ config.action_mailer.raise_delivery_errors = false
+
+ # Print deprecation notices to the Rails logger
+ config.active_support.deprecation = :log
+
+ # Only use best-standards-support built into browsers
+ config.action_dispatch.best_standards_support = :builtin
+end
+
49 spec/dummy/config/environments/production.rb
@@ -0,0 +1,49 @@
+Dummy::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # The production environment is meant for finished, "live" apps.
+ # Code is not reloaded between requests
+ config.cache_classes = true
+
+ # Full error reports are disabled and caching is turned on
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Specifies the header that your server uses for sending files
+ config.action_dispatch.x_sendfile_header = "X-Sendfile"
+
+ # For nginx:
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
+
+ # If you have no front-end server that supports something like X-Sendfile,
+ # just comment this out and Rails will serve the files
+
+ # See everything in the log (default is :info)
+ # config.log_level = :debug
+
+ # Use a different logger for distributed setups
+ # config.logger = SyslogLogger.new
+
+ # Use a different cache store in production
+ # config.cache_store = :mem_cache_store
+
+ # Disable Rails's static asset server
+ # In production, Apache or nginx will already do this
+ config.serve_static_assets = false
+
+ # Enable serving of images, stylesheets, and javascripts from an asset server
+ # config.action_controller.asset_host = "http://assets.example.com"
+
+ # Disable delivery errors, bad email addresses will be ignored
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable threaded mode
+ # config.threadsafe!
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation can not be found)
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners
+ config.active_support.deprecation = :notify
+end
35 spec/dummy/config/environments/test.rb
@@ -0,0 +1,35 @@
+Dummy::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment
+ config.action_controller.allow_forgery_protection = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Use SQL instead of Active Record's schema dumper when creating the test database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types
+ # config.active_record.schema_format = :sql
+
+ # Print deprecation notices to the stderr
+ config.active_support.deprecation = :stderr
+end
7 spec/dummy/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
10 spec/dummy/config/initializers/inflections.rb
@@ -0,0 +1,10 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format
+# (all these examples are active by default):
+# ActiveSupport::Inflector.inflections do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
5 spec/dummy/config/initializers/mime_types.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
+# Mime::Type.register_alias "text/html", :iphone
7 spec/dummy/config/initializers/secret_token.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+Dummy::Application.config.secret_token = 'd8c8c5941e7498f0125435caa175cbb17244f1f40ddd4ff804d297714f6f5d4f3501504a9fda81ff91f57e38927848d29f824fd28334231593c610291dd76796'
8 spec/dummy/config/initializers/session_store.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+Dummy::Application.config.session_store :cookie_store, :key => '_dummy_session'
+
+# Use the database for sessions instead of the cookie-based default,
+# which shouldn't be used to store highly confidential information
+# (create the session table with "rails generate session_migration")
+# Dummy::Application.config.session_store :active_record_store
5 spec/dummy/config/locales/en.yml
@@ -0,0 +1,5 @@
+# Sample localization file for English. Add more files in this directory for other locales.
+# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+
+en:
+ hello: "Hello world"
60 spec/dummy/config/routes.rb
@@ -0,0 +1,60 @@
+Dummy::Application.routes.draw do
+ resources :users
+
+ # The priority is based upon order of creation:
+ # first created -> highest priority.
+
+ # Sample of regular route:
+ # match 'products/:id' => 'catalog#view'
+ # Keep in mind you can assign values other than :controller and :action
+
+ # Sample of named route:
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
+ # This route can be invoked with purchase_url(:id => product.id)
+
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
+ # resources :products
+
+ # Sample resource route with options:
+ # resources :products do
+ # member do
+ # get 'short'
+ # post 'toggle'
+ # end
+ #
+ # collection do
+ # get 'sold'
+ # end
+ # end
+
+ # Sample resource route with sub-resources:
+ # resources :products do
+ # resources :comments, :sales
+ # resource :seller
+ # end
+
+ # Sample resource route with more complex sub-resources
+ # resources :products do
+ # resources :comments
+ # resources :sales do
+ # get 'recent', :on => :collection
+ # end
+ # end
+
+ # Sample resource route within a namespace:
+ # namespace :admin do
+ # # Directs /admin/products/* to Admin::ProductsController
+ # # (app/controllers/admin/products_controller.rb)
+ # resources :products
+ # end
+
+ # You can have the root of your site routed with "root"
+ # just remember to delete public/index.html.
+ root :to => "users#index"
+
+ # See how all your routes lay out with "rake routes"
+
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
+ # Note: This route will make all actions in every controller accessible via GET requests.
+ # match ':controller(/:action(/:id(.:format)))'
+end
13 spec/dummy/db/migrate/20110228120600_create_users.rb
@@ -0,0 +1,13 @@
+class CreateUsers < ActiveRecord::Migration
+ def self.up
+ create_table :users do |t|
+ t.string :name
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :users
+ end
+end
37 spec/dummy/db/migrate/20110302132940_create_mailboxer.rb
@@ -0,0 +1,37 @@
+class CreateMailboxer < ActiveRecord::Migration
+ def self.up
+ create_table :mailboxer_conversations do |t|
+ t.column :subject, :string, :default => ""
+ t.column :created_at, :datetime, :null => false
+ end
+ create_table :mailboxer_mails do |t|
+ t.column :user_id, :integer, :null => false
+ t.column :mailboxer_message_id, :integer, :null => false
+ #t.column :mailboxer_conversation_id, :integer
+ t.column :read, :boolean, :default => false
+ t.column :trashed, :boolean, :default => false
+ t.column :mailbox_type, :string, :limit => 25
+ t.column :created_at, :datetime, :null => false
+ end
+ create_table :mailboxer_messages do |t|
+ t.column :body, :text
+ t.column :subject, :string, :default => ""
+ t.column :headers, :text
+ t.column :sender_id, :integer, :null => false
+ t.column :mailboxer_conversation_id, :integer
+ t.column :sent, :boolean, :default => false
+ t.column :created_at, :datetime, :null => false
+ end
+ create_table :mailboxer_recipients, :id => false do |t|
+ t.column :mailboxer_message_id, :integer, :null => false
+ t.column :recipient_id, :integer, :null => false
+ end
+ end
+
+ def self.down
+ drop_table :mailboxer_mails
+ drop_table :mailboxer_conversations
+ drop_table :mailboxer_recipients
+ drop_table :mailboxer_messages
+ end
+end
50 spec/dummy/db/schema.rb
@@ -0,0 +1,50 @@
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 20110302132940) do
+
+ create_table "mailboxer_conversations", :force => true do |t|
+ t.string "subject", :default => ""
+ t.datetime "created_at", :null => false
+ end
+
+ create_table "mailboxer_mails", :force => true do |t|
+ t.integer "user_id", :null => false
+ t.integer "mailboxer_message_id", :null => false
+ t.boolean "read", :default => false
+ t.boolean "trashed", :default => false
+ t.string "mailbox_type", :limit => 25
+ t.datetime "created_at", :null => false
+ end
+
+ create_table "mailboxer_messages", :force => true do |t|
+ t.text "body"
+ t.string "subject", :default => ""
+ t.text "headers"
+ t.integer "sender_id", :null => false
+ t.integer "mailboxer_conversation_id"
+ t.boolean "sent", :default => false
+ t.datetime "created_at", :null => false
+ end
+
+ create_table "mailboxer_recipients", :id => false, :force => true do |t|
+ t.integer "mailboxer_message_id", :null => false
+ t.integer "recipient_id", :null => false
+ end
+
+ create_table "users", :force => true do |t|
+ t.string "name"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+end
26 spec/dummy/public/404.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The page you were looking for doesn't exist (404)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/404.html -->
+ <div class="dialog">
+ <h1>The page you were looking for doesn't exist.</h1>
+ <p>You may have mistyped the address or the page may have moved.</p>
+ </div>
+</body>
+</html>
26 spec/dummy/public/422.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The change you wanted was rejected (422)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/422.html -->
+ <div class="dialog">
+ <h1>The change you wanted was rejected.</h1>
+ <p>Maybe you tried to change something you didn't have access to.</p>
+ </div>
+</body>
+</html>
26 spec/dummy/public/500.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>We're sorry, but something went wrong (500)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/500.html -->
+ <div class="dialog">
+ <h1>We're sorry, but something went wrong.</h1>
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
+ </div>
+</body>
+</html>
0 spec/dummy/public/favicon.ico
No changes.
2 spec/dummy/public/javascripts/application.js
@@ -0,0 +1,2 @@
+// Place your application-specific JavaScript functions and classes here
+// This file is automatically included by javascript_include_tag :defaults
965 spec/dummy/public/javascripts/controls.js
@@ -0,0 +1,965 @@
+// script.aculo.us controls.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009
+
+// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005-2009 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005-2009 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+// Rob Wills
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+ throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { };
+Autocompleter.Base = Class.create({
+ baseInitialize