diff options
34 files changed, 508 insertions, 156 deletions
@@ -2,4 +2,5 @@ .bundle Gemfile.lock pkg/* -.DS_Store
\ No newline at end of file +.DS_Store +.rvmrc
\ No newline at end of file @@ -6,6 +6,10 @@ Inboxes is a young messaging system for Rails app. It: - read/unread discussions counter - any user can be invited to discussion by the member of this discussion, so you can chat with unlimited number of users +##Upgrading from 0.1 to current version (0.2) + +**Run `rails generate inboxes:upgrade_discussible` and then roll up the migration. Your DB is upgraded!** + ##Requirements and recommendations Inboxes requires Rails 3.x and [Devise](https://github.com/plataformatec/devise) for user identification (surely, messaging system is not possible without users). Now the gem is tested only with Ruby 1.8.7 and REE. @@ -14,13 +18,15 @@ We recommend to use Inboxes with [Faye](https://github.com/jcoglan/faye), becaus Remember that unfortunately, Inboxes reserve 3 resources names: Discussion, Message and Speaker. +Since version 0.2.0, it is possible to add `has_inboxes` option to any model. For instance, it can be `Person` or `Teacher`. + ##Installation *Make sure that Devise is already installed and configured in your app!* -1. Add `gem "inboxes", "~> 0.1.2"` to the `Gemfile` and run `bundle install` +1. Add `gem "inboxes", "~> 0.2.0"` to the `Gemfile` and run `bundle install` 2. Execute `rails generate inboxes:install`. This command will generate migration for messaging system. Don't forget to run migrations: `rake db:migrate` -3. Add `inboxes` to your User model like [here](https://gist.github.com/1330080) +3. Add `has_inboxes` to your User model like [here](https://gist.github.com/1330080). 4. Now Inboxes are ready to use. Open `http://yoursite.dev/discussions` to see the list of discussions. You can start new one. Default Inboxes views are ugly, so you can copy into your app and make anything with them: `rails generate inboxes:views` @@ -35,15 +41,17 @@ By default, the gem provides localized phrases for Russian and English languages You can watch the demo of integration [on YouTube](http://youtu.be/c12gey9DvyU) 1. Add `gem "faye"` to your Gemfile and run `bundle install`. Install Faye by [the screencast](http://railscasts.com/episodes/260-messaging-with-faye) -2. Create `messaging.js` in `app/assets/javascripts/` with these line: `= require inboxes/faye` +2. Create `messaging.js` in `app/assets/javascripts/` with this line: `//= require inboxes/faye` 3. Copy or replace 2 views from Inboxes example app to your application: [app/views/inboxes/messages/_form](https://github.com/kirs/inboxes-app/blob/master/app/views/inboxes/messages/_form.html.haml) and [app/views/inboxes/messages/create](https://github.com/kirs/inboxes-app/blob/master/app/views/inboxes/messages/create.js.erb) - + 4. Add config parameters to your application config (last 2 are not necessary): - -`config.inboxes.faye_enabled = true` -`config.inboxes.faye_host = "inboxes-app.dev" # localhost by default` -`config.inboxes.faye_port = 9292 # 9292 by default` + +```ruby +config.inboxes.faye_enabled = true +config.inboxes.faye_host = "inboxes-app.dev" # localhost by default +config.inboxes.faye_port = 9292 # 9292 by default +``` 5. Faye installation is finished. If you have any troubles, check the [example app](https://github.com/kirs/inboxes-app/) @@ -57,9 +65,16 @@ You can read more about Faye on it's [official page](http://faye.jcoglan.com/). ##Todo - Add RSpec tests +- Add ability to inherit Inboxes controllers +- Add Pusher capability +- Email notifications and the ability to answer received emails like in Github issues (#7) -##Authors +##Contributors - [Kir Shatrov](https://github.com/kirs/) (Evrone Company) +- [Andrey Ognevsky](https://github.com/ognevsy/) (Evrone Company) +- [Alexander Brodyanoj](https://github.com/dom1nga) +- [Dmitriy Kiriyenko](https://github.com/dmitriy-kiriyenko) +- [Alexey Poimtsev](https://github.com/alec-c4) ([http://progress-engine.ru/](Progress Engine)) -##Feel free for pull requests!
\ No newline at end of file +##Feel free for pull requests! diff --git a/app/controllers/inboxes/base_controller.rb b/app/controllers/inboxes/base_controller.rb index 680e38c..3e3f8dc 100644 --- a/app/controllers/inboxes/base_controller.rb +++ b/app/controllers/inboxes/base_controller.rb @@ -1,13 +1,11 @@ class Inboxes::BaseController < ApplicationController private - + def init_discussion @discussion = Discussion.find(params[:discussion_id]) end - - # Needs to be overriden so that we use Spree's Ability rather than anyone else's. + def current_ability - # raise "Loading Ability" @current_ability ||= Inboxes::Ability.new(current_user) end end
\ No newline at end of file diff --git a/app/controllers/inboxes/discussions_controller.rb b/app/controllers/inboxes/discussions_controller.rb index c03a062..52655e2 100644 --- a/app/controllers/inboxes/discussions_controller.rb +++ b/app/controllers/inboxes/discussions_controller.rb @@ -1,57 +1,41 @@ class Inboxes::DiscussionsController < Inboxes::BaseController load_and_authorize_resource - # before_filter :authenticate_user! - # before_filter :init_and_check_permissions, :only => :show before_filter :load_and_check_discussion_recipient, :only => [:create, :new] - + def index @discussions = current_user.discussions end - - # GET /discussions/1 - # GET /discussions/1.json + def show # @discussion = Discussion.includes(:messages, :speakers).find(params[:id]) @discussion.mark_as_read_for(current_user) end - - # GET /discussions/new - # GET /discussions/new.json + def new - # @discussion = Discussion.new @discussion.messages.build end - # POST /discussions - # POST /discussions.json def create - # @discussion = Discussion.new(params[:discussion]) @discussion.add_recipient_token current_user.id - + @discussion.messages.each do |m| m.discussion = @discussion m.user = current_user end - + if @discussion.save redirect_to @discussion, :notice => t("inboxes.discussions.started") else render :action => "new" end end - + private - # def init_and_check_permissions - # @discussion = Discussion.includes(:messages, :speakers).find(params[:id]) - # redirect_to discussions_url, :notice => t("inboxes.discussions.can_not_participate") unless @discussion.can_participate?(current_user) - # end - def load_and_check_discussion_recipient # initializing model for new and create actions @discussion = Discussion.new(params[:discussion].presence || {}) - # @discussion.recipient_tokens = params[:recipients] if params[:recipients] # pre-population - + # checking if discussion with this user already exists if @discussion.recipient_ids && @discussion.recipient_ids.size == 1 user = User.find(@discussion.recipient_ids.first) @@ -61,8 +45,8 @@ class Inboxes::DiscussionsController < Inboxes::BaseController @discussion.messages.each do |message| Message.create(:discussion => discussion, :user => current_user, :body => message.body) if message.body end - # перекидываем на нее - redirect_to discussion_url(discussion), :notice => t("inboxes.discussions.exists", :user => user[Inboxes::config.user_name]) + # redirecting to this discussion page + redirect_to discussion_url(discussion), :notice => t("inboxes.discussions.already_exists", :user => user[Inboxes::config.user_name]) end end end diff --git a/app/controllers/inboxes/messages_controller.rb b/app/controllers/inboxes/messages_controller.rb index 4089b13..697592a 100644 --- a/app/controllers/inboxes/messages_controller.rb +++ b/app/controllers/inboxes/messages_controller.rb @@ -3,12 +3,12 @@ class Inboxes::MessagesController < Inboxes::BaseController # load_and_authorize_resource load_and_authorize_resource :discussion load_resource :message, :through => :discussion, :shallow => true - + def create @message.user = current_user @message.discussion = @discussion @message.save - + respond_to do |format| format.html { redirect_to @message.discussion } format.js @@ -16,7 +16,7 @@ class Inboxes::MessagesController < Inboxes::BaseController end # private - # + # # def init_and_check_permissions # @discussion = Discussion.find(params[:discussion_id]) # redirect_to discussions_url, :notice => t("inboxes.discussions.can_not_participate") unless @discussion.can_participate?(current_user) diff --git a/app/controllers/inboxes/speakers_controller.rb b/app/controllers/inboxes/speakers_controller.rb index 96a3049..30c0b7f 100644 --- a/app/controllers/inboxes/speakers_controller.rb +++ b/app/controllers/inboxes/speakers_controller.rb @@ -3,14 +3,14 @@ class Inboxes::SpeakersController < Inboxes::BaseController load_and_authorize_resource :discussion load_resource :speaker, :through => :discussion, :shallow => true # load_and_authorize_resource - + def create raise ActiveRecord::RecordNotFound unless params[:speaker] && params[:speaker][:user_id] @user = User.find(params[:speaker][:user_id]) flash[:notice] = t("inboxes.speakers.added") if @discussion.add_speaker(@user) redirect_to @discussion end - + def destroy @speaker = Speaker.find(params[:id]) @speaker.destroy diff --git a/app/models/discussion.rb b/app/models/discussion.rb index aa4ce3e..675547d 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -1,19 +1,19 @@ - class Discussion < ActiveRecord::Base attr_accessor :recipient_tokens, :recipient_ids attr_reader :recipient_ids # paginates_per 10 - # создатель + # creater has_many :messages, :dependent => :destroy - # участники + belongs_to :discussable, :polymorphic => true + + # participants of discussion (speakers) has_many :speakers, :dependent => :destroy has_many :users, :through => :speakers - # отметки о прочтении юзеров - + # marks about read/unread scope :unread_for, (lambda do |user_or_user_id| user = user_or_user_id.is_a?(User) ? user_or_user_id.id : user_or_user_id joins(:speakers).where("discussions.updated_at >= speakers.updated_at AND speakers.user_id = ?", user) @@ -21,12 +21,10 @@ class Discussion < ActiveRecord::Base accepts_nested_attributes_for :messages - validate :check_that_has_at_least_two_users # не даем создать дискуссию, у которой нет получателей + validate :check_that_has_at_least_two_users # don't allow to create discussion, if there is no creator - # добавляем записи об указанных собеседников + # mark as read after_save(:on => :create) do - # Rails.logger.info("Repicients ids: #{recipient_ids.inspect}") - if recipient_ids.kind_of?(Array) recipient_ids.uniq! recipient_ids.each do |id| @@ -55,38 +53,30 @@ class Discussion < ActiveRecord::Base end def user_invited_at(user) - speaker = find_speaker_by_user(user) - speaker.created_at + find_speaker_by_user(user).created_at end def can_participate?(user) - speaker = find_speaker_by_user(user) - speaker ? true : false + !!find_speaker_by_user(user) end - # проверяет, есть ли уже беседа между пользователями - # TODO вынести в отдельный метод а в этом возращать true/false, а то неправославно как-то + # don't allow to create discussion with user, if discussion with this user already exists + # TODO move to separated method and return boolean value def self.find_between_users(user, user2) dialog = nil discussions = self.joins(:speakers).includes(:users).where("speakers.user_id = ?", user.id) Rails.logger.info "Searching for ids: #{user.id}, #{user2.id}" discussions.each do |discussion| - dialog = discussion if discussion.private? && ((discussion.users.first == user && discussion.users.last == user2) || (discussion.users.first == user2 && discussion.users.last == user)) + dialog = discussion if discussion.private? && ([discussion.users.first, discussion.users.last] - [user, user2]).empty? end dialog end - # приватная/групповая + # private/group discussion def private? self.users.size <= 2 end - # дата последнего сообщения в дискуссии - # def last_message_at - # self.messages.last ? self.messages.last.created_at : nil - # end - - # проверка, является ли дискуссия непрочитанной для пользователя def unread_for?(user) speaker = find_speaker_by_user(user) if speaker @@ -96,21 +86,26 @@ class Discussion < ActiveRecord::Base end end - # пометить как прочитанная + # return amount of unreaded messages for current discussion + def unread_messages_count_for(user) + speaker = find_speaker_by_user(user) + messages.where("updated_at > ?", speaker.updated_at ).where("user_id != ?", speaker.id ).count + end + def mark_as_read_for(user) speaker = Speaker.find_or_create_by_user_id_and_discussion_id(user.id, self.id) # flag.update_attributes(:updat => Time.zone.now) speaker.touch end - + def find_speaker_by_user user Speaker.find_by_discussion_id_and_user_id(self.id, user.id) end - + private def check_that_has_at_least_two_users errors.add :recipient_tokens, t("inboxes.discussions.choose_at_least_one_recipient") if !self.recipient_ids || self.recipient_ids.size < 2 end -end
\ No newline at end of file +end diff --git a/app/models/inboxes/ability.rb b/app/models/inboxes/ability.rb index 4d8408f..deb3d81 100644 --- a/app/models/inboxes/ability.rb +++ b/app/models/inboxes/ability.rb @@ -15,20 +15,20 @@ module Inboxes # and therefore should be easy to test in isolation. def self.register_ability(ability) self.abilities.add(ability) - + end def initialize(user) # raise "Initializing 3rd patry" # self.clear_aliased_actions - + # can [:index, :create], Discussion # can :read, Discussion do |discussion| # discussion.can_participate?(user) # end #include any abilities registered by extensions, etc. - + Ability.abilities.each do |clazz| ability = clazz.send(:new, user) @rules = rules + ability.send(:rules) diff --git a/app/models/message.rb b/app/models/message.rb index b7f9b12..436775b 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -1,22 +1,21 @@ class Message < ActiveRecord::Base default_scope order(:created_at) - - belongs_to :discussion, :counter_cache => true + + belongs_to :discussion, :counter_cache => true, :touch => true belongs_to :user - + validates :user, :discussion, :body, :presence => true - - after_save :touch_discussion_and_mark_as_read - + + after_save :mark_discussion_as_read + def visible_for? user self.created_at.to_i >= self.discussion.user_invited_at(user).to_i end - + private - - def touch_discussion_and_mark_as_read - self.discussion.touch + + def mark_discussion_as_read self.discussion.mark_as_read_for(self.user) end end diff --git a/app/models/speaker.rb b/app/models/speaker.rb index 0ed9cdd..4d8795b 100644 --- a/app/models/speaker.rb +++ b/app/models/speaker.rb @@ -1,16 +1,16 @@ class Speaker < ActiveRecord::Base belongs_to :user belongs_to :discussion - + validates_uniqueness_of :user_id, :scope => :discussion_id validates :user, :discussion, :presence => true - + after_destroy :destroy_discussion - + private - + def destroy_discussion self.discussion.destroy unless self.discussion.speakers.any? end - + end diff --git a/app/views/inboxes/discussions/_form.html.haml b/app/views/inboxes/discussions/_form.html.haml index 303f0bb..800004b 100644 --- a/app/views/inboxes/discussions/_form.html.haml +++ b/app/views/inboxes/discussions/_form.html.haml @@ -8,5 +8,5 @@ = j.label :body %br = j.text_area :body - + %p= f.submit
\ No newline at end of file diff --git a/app/views/inboxes/discussions/index.html.haml b/app/views/inboxes/discussions/index.html.haml index 86ae297..433e929 100644 --- a/app/views/inboxes/discussions/index.html.haml +++ b/app/views/inboxes/discussions/index.html.haml @@ -11,7 +11,10 @@ %td = link_to discussion.users.collect{|u| u[Inboxes::config.user_name]}.join(', '), discussion %td - = discussion.unread_for?(current_user) ? "Yes" : "No" - + - if discussion.unread_for?(current_user) + = pluralize(discussion.unread_messages_count_for(current_user), "message") + - else + No unread messages + %p - = link_to "Create new", new_discussion_path
\ No newline at end of file + = link_to "Create new", new_discussion_path diff --git a/app/views/inboxes/discussions/show.html.haml b/app/views/inboxes/discussions/show.html.haml index a910b51..50f5d19 100644 --- a/app/views/inboxes/discussions/show.html.haml +++ b/app/views/inboxes/discussions/show.html.haml @@ -16,13 +16,13 @@ = f.label :user_id, "Add speaker" = f.collection_select :user_id, available_users, :id, :name = f.submit "Add" - - + + %h3 Messages #messages_box = render @discussion.messages, :as => :message - + %h3 Add message = render "inboxes/messages/form" diff --git a/config/locales/en.yml b/config/locales/en.yml index 3bcda3c..a9a810d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -9,7 +9,7 @@ en: removed: "Speaker was removed from discussion" discussions: started: "Discussion started" - leaved: "You leaved the discussion" - exists: "Discussion between you and %{user} already exists" + leaved: "You leave the discussion" + already_exists: "Discussion between you and %{user} already exists" can_not_participate: "You are not listed in this discussion" choose_at_least_one_recipient: "You should choose at least one recipient of discussion"
\ No newline at end of file diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 1258f9f..1e311cd 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -9,7 +9,7 @@ ru: discussions: started: "Чат начат" leaved: "Вы покинули дискуссию" - exists: "Дискуссия между вами и %{user} уже существует" + already_exists: "Дискуссия между вами и %{user} уже существует" can_not_participate: "Вы не состоите в этой дискуссии" choose_at_least_one_recipient: "Укажите хотя бы одного получателя" speakers: diff --git a/config/routes.rb b/config/routes.rb index 9da2d4a..3ecb989 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ Rails.application.routes.draw do - + resources :discussions, :except => :edit, :module => :inboxes do resources :messages, :only => [:create, :index] resources :speakers, :only => [:create, :destroy] @@ -7,5 +7,5 @@ Rails.application.routes.draw do post 'leave' end end - + end
\ No newline at end of file diff --git a/inboxes.gemspec b/inboxes.gemspec index 9f7a2ba..82ec3c3 100644 --- a/inboxes.gemspec +++ b/inboxes.gemspec @@ -21,9 +21,11 @@ Gem::Specification.new do |s| # specify any dependencies here; for example: # s.add_development_dependency "ruby-debug" s.add_runtime_dependency "haml-rails" - s.add_runtime_dependency "cancan", ['>= 0'] - # s.add_runtime_dependency "inherited_resources" + s.add_runtime_dependency "devise" + s.add_runtime_dependency "rails" + s.add_runtime_dependency "cancan" + s.add_development_dependency "sqlite3" # s.add_development_dependency 'dm-sqlite-adapter', ['>= 1.1.0'] s.add_development_dependency 'rspec', ['>= 0'] s.add_development_dependency 'factory_girl', ['~> 1.2'] diff --git a/lib/generators/inboxes/install_generator.rb b/lib/generators/inboxes/install_generator.rb index d5d2a97..48ac223 100644 --- a/lib/generators/inboxes/install_generator.rb +++ b/lib/generators/inboxes/install_generator.rb @@ -5,19 +5,15 @@ module Inboxes module Generators class InstallGenerator < Rails::Generators::Base include Rails::Generators::Migration - + source_root File.expand_path("../templates", __FILE__) - - # desc "Generates migration for Discussion, Message, Speaker and DiscussionView models" + + desc "Generates migration for Inboxes" def self.orm Rails::Generators.options[:rails][:orm] end - # def self.source_root - # File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) ) - # end - def self.orm_has_migration? [:active_record].include? orm end diff --git a/lib/generators/inboxes/templates/install.rb b/lib/generators/inboxes/templates/install.rb index 91fded7..e89c128 100644 --- a/lib/generators/inboxes/templates/install.rb +++ b/lib/generators/inboxes/templates/install.rb @@ -2,6 +2,7 @@ class InstallInboxes < ActiveRecord::Migration def self.up create_table :discussions do |t| t.integer :messages_count, :default => 0 # counter cache + t.references :discussable t.timestamps end @@ -9,14 +10,14 @@ class InstallInboxes < ActiveRecord::Migration t.references :user t.references :discussion t.text :body - + t.timestamps end - + create_table :speakers do |t| t.references :user t.references :discussion - + t.timestamps end end @@ -26,4 +27,4 @@ class InstallInboxes < ActiveRecord::Migration drop_table :discussions drop_table :messages end -end
\ No newline at end of file +end diff --git a/lib/generators/inboxes/templates/upgrade_discussible.rb b/lib/generators/inboxes/templates/upgrade_discussible.rb new file mode 100644 index 0000000..dc39dd9 --- /dev/null +++ b/lib/generators/inboxes/templates/upgrade_discussible.rb @@ -0,0 +1,9 @@ +class UpgradeDiscussibleInboxes < ActiveRecord::Migration + def self.change + add_column :discussions, :discussible_id, :integer + end + + def self.down + remove_column :discussions, :discussible_id + end +end diff --git a/lib/generators/inboxes/upgrade_discussible_generator.rb b/lib/generators/inboxes/upgrade_discussible_generator.rb new file mode 100644 index 0000000..475f668 --- /dev/null +++ b/lib/generators/inboxes/upgrade_discussible_generator.rb @@ -0,0 +1,36 @@ +require 'rails/generators' +require 'rails/generators/migration' + +module Inboxes + module Generators + class UpgradeDiscussibleGenerator < Rails::Generators::Base + include Rails::Generators::Migration + + source_root File.expand_path("../templates", __FILE__) + + desc "Generates migration for Inboxes 0.2.0 update" + + def self.orm + Rails::Generators.options[:rails][:orm] + end + + def self.orm_has_migration? + [:active_record].include? orm + end + + def self.next_migration_number(dirname) + if ActiveRecord::Base.timestamped_migrations + migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i + migration_number += 1 + migration_number.to_s + else + "%.3d" % (current_migration_number(dirname) + 1) + end + end + + def copy_migration + migration_template 'upgrade_discussible.rb', 'db/migrate/upgrade_discussible_inboxes.rb' + end + end + end +end diff --git a/lib/generators/inboxes/views_generator.rb b/lib/generators/inboxes/views_generator.rb index 117c05d..e9a8818 100644 --- a/lib/generators/inboxes/views_generator.rb +++ b/lib/generators/inboxes/views_generator.rb @@ -5,7 +5,7 @@ module Inboxes class ViewsGenerator < Rails::Generators::Base source_root File.expand_path('../../../../app/views', __FILE__) #class_option :template_engine, :type => :string, :aliases => '-e', :desc => 'Template engine for the views. Available options are "erb" and "haml".' - + # TODO support of both haml and erb def copy_or_fetch filename_pattern = File.join self.class.source_root, "*" #/*.html.#{template_engine}" diff --git a/lib/inboxes.rb b/lib/inboxes.rb index f1b9c7a..af88b0c 100644 --- a/lib/inboxes.rb +++ b/lib/inboxes.rb @@ -3,10 +3,9 @@ require "inboxes/railtie" require "inboxes/ability" require "inboxes/engine" require "inboxes/active_record_extension" - +require "cancan" module Inboxes - def self.configure(&block) yield @config ||= Inboxes::Configuration.new end @@ -25,11 +24,10 @@ module Inboxes config_accessor :faye_enabled def param_name - config.param_name.respond_to?(:call) ? config.param_name.call() : config.param_name + config.param_name.respond_to?(:call) ? config.param_name.call() : config.param_name end end - + # adding method inboxes for models ActiveRecord::Base.extend(Inboxes::ActiveRecordExtension) - end diff --git a/lib/inboxes/ability.rb b/lib/inboxes/ability.rb index 3ed216b..c9f8d6b 100644 --- a/lib/inboxes/ability.rb +++ b/lib/inboxes/ability.rb @@ -13,17 +13,17 @@ module Inboxes discussion.can_participate?(user) end end - + # Message # can :create, Message do |message| # message.discussion.can_participate?(user) # end - # + # # # Speaker # can [:create, :destroy], Speaker do |speaker| # speaker.discussion.can_participate?(user) # end end end - + end
\ No newline at end of file diff --git a/lib/inboxes/active_record_extension.rb b/lib/inboxes/active_record_extension.rb index 34fd387..b3c3231 100644 --- a/lib/inboxes/active_record_extension.rb +++ b/lib/inboxes/active_record_extension.rb @@ -3,9 +3,9 @@ module Inboxes def has_inboxes(options = {}) # field = options[:as] || name # prefix = options[:prefix] || "with" - + has_many :speakers, :dependent => :destroy - has_many :discussions, :through => :speakers + has_many :discussions, :as => :discussable, :through => :speakers end end -end
\ No newline at end of file +end diff --git a/lib/inboxes/engine.rb b/lib/inboxes/engine.rb index 01f63b5..b61eebc 100644 --- a/lib/inboxes/engine.rb +++ b/lib/inboxes/engine.rb @@ -5,7 +5,7 @@ module Inboxes def self.activate Ability.register_ability(InboxesAbility) end - + config.to_prepare &method(:activate).to_proc end end
\ No newline at end of file diff --git a/lib/inboxes/railtie.rb b/lib/inboxes/railtie.rb index 673784e..1626b20 100644 --- a/lib/inboxes/railtie.rb +++ b/lib/inboxes/railtie.rb @@ -4,7 +4,7 @@ require "inboxes/ability" module Inboxes class Railtie < ::Rails::Railtie config.inboxes = ActiveSupport::OrderedOptions.new - + initializer "inboxes.configure" do |app| Inboxes.configure do |config| config.user_name = app.config.inboxes[:user_name] || "email" @@ -15,7 +15,7 @@ module Inboxes # app.config.middleware.insert_before "::Rails::Rack::Logger", "Inboxes::Middleware" end - + # def self.activate # Ability.register_ability(InboxesAbility) # end diff --git a/lib/inboxes/version.rb b/lib/inboxes/version.rb index 5470b38..ee23565 100644 --- a/lib/inboxes/version.rb +++ b/lib/inboxes/version.rb @@ -1,3 +1,3 @@ module Inboxes - VERSION = "0.1.5" + VERSION = "0.2.0" end diff --git a/log/development.log b/log/development.log new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/log/development.log diff --git a/spec/devise_config.rb b/spec/devise_config.rb new file mode 100644 index 0000000..5e86e34 --- /dev/null +++ b/spec/devise_config.rb @@ -0,0 +1,210 @@ +# Use this hook to configure devise mailer, warden hooks and so forth. The first +# four configuration values can also be set straight in your models. +Devise.setup do |config| + # ==> Mailer Configuration + # Configure the e-mail address which will be shown in Devise::Mailer, + # note that it will be overwritten if you use your own mailer class with default "from" parameter. + config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" + + # Configure the class responsible to send e-mails. + # config.mailer = "Devise::Mailer" + + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default) and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require 'devise/orm/active_record' + + # ==> Configuration for any authentication mechanism + # Configure which keys are used when authenticating a user. The default is + # just :email. You can configure it to use [:username, :subdomain], so for + # authenticating a user, both parameters are required. Remember that those + # parameters are used only when authenticating and not when retrieving from + # session. If you need permissions, you should implement that in a before filter. + # You can also supply a hash where the value is a boolean determining whether + # or not authentication should be aborted when the value is not present. + # config.authentication_keys = [ :email ] + + # Configure parameters from the request object used for authentication. Each entry + # given should be a request method and it will automatically be passed to the + # find_for_authentication method and considered in your model lookup. For instance, + # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. + # The same considerations mentioned for authentication_keys also apply to request_keys. + # config.request_keys = [] + + # Configure which authentication keys should be case-insensitive. + # These keys will be downcased upon creating or modifying a user and when used + # to authenticate or find a user. Default is :email. + config.case_insensitive_keys = [ :email ] + + # Configure which authentication keys should have whitespace stripped. + # These keys will have whitespace before and after removed upon creating or + # modifying a user and when used to authenticate or find a user. Default is :email. + config.strip_whitespace_keys = [ :email ] + + # Tell if authentication through request.params is enabled. True by default. + # config.params_authenticatable = true + + # Tell if authentication through HTTP Basic Auth is enabled. False by default. + # config.http_authenticatable = false + + # If http headers should be returned for AJAX requests. True by default. + # config.http_authenticatable_on_xhr = true + + # The realm used in Http Basic Authentication. "Application" by default. + # config.http_authentication_realm = "Application" + + # It will change confirmation, password recovery and other workflows + # to behave the same regardless if the e-mail provided was right or wrong. + # Does not affect registerable. + # config.paranoid = true + + # ==> Configuration for :database_authenticatable + # For bcrypt, this is the cost for hashing the password and defaults to 10. If + # using other encryptors, it sets how many times you want the password re-encrypted. + # + # Limiting the stretches to just one in testing will increase the performance of + # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use + # a value less than 10 in other environments. + config.stretches = Rails.env.test? ? 1 : 10 + + # Setup a pepper to generate the encrypted password. + # config.pepper = "9a86f39fcb72573a367c4fcde09b1ae3fe81c3c2c5858c6024033360b39a0f46ab72c8aade6453e96dfae546262c0336372c19518600367fc8b94e9bb4d5db15" + + # ==> Configuration for :confirmable + # A period that the user is allowed to access the website even without + # confirming his account. For instance, if set to 2.days, the user will be + # able to access the website for two days without confirming his account, + # access will be blocked just in the third day. Default is 0.days, meaning + # the user cannot access the website without confirming his account. + # config.confirm_within = 2.days + + # Defines which key will be used when confirming an account + # config.confirmation_keys = [ :email ] + + # ==> Configuration for :rememberable + # The time the user will be remembered without asking for credentials again. + # config.remember_for = 2.weeks + + # If true, a valid remember token can be re-used between multiple browsers. + # config.remember_across_browsers = true + + # If true, extends the user's remember period when remembered via cookie. + # config.extend_remember_period = false + + # If true, uses the password salt as remember token. This should be turned + # to false if you are not using database authenticatable. + config.use_salt_as_remember_token = true + + # Options to be passed to the created cookie. For instance, you can set + # :secure => true in order to force SSL only cookies. + # config.cookie_options = {} + + # ==> Configuration for :validatable + # Range for password length. Default is 6..128. + # config.password_length = 6..128 + + # Email regex used to validate email formats. It simply asserts that + # an one (and only one) @ exists in the given string. This is mainly + # to give user feedback and not to assert the e-mail validity. + # config.email_regexp = /\A[^@]+@[^@]+\z/ + + # ==> Configuration for :timeoutable + # The time you want to timeout the user session without activity. After this + # time the user will be asked for credentials again. Default is 30 minutes. + # config.timeout_in = 30.minutes + + # ==> Configuration for :lockable + # Defines which strategy will be used to lock an account. + # :failed_attempts = Locks an account after a number of failed attempts to sign in. + # :none = No lock strategy. You should handle locking by yourself. + # config.lock_strategy = :failed_attempts + + # Defines which key will be used when locking and unlocking an account + # config.unlock_keys = [ :email ] + + # Defines which strategy will be used to unlock an account. + # :email = Sends an unlock link to the user email + # :time = Re-enables login after a certain amount of time (see :unlock_in below) + # :both = Enables both strategies + # :none = No unlock strategy. You should handle unlocking by yourself. + # config.unlock_strategy = :both + + # Number of authentication tries before locking an account if lock_strategy + # is failed attempts. + # config.maximum_attempts = 20 + + # Time interval to unlock the account if :time is enabled as unlock_strategy. + # config.unlock_in = 1.hour + + # ==> Configuration for :recoverable + # + # Defines which key will be used when recovering the password for an account + # config.reset_password_keys = [ :email ] + + # Time interval you can reset your password with a reset password key. + # Don't put a too small interval or your users won't have the time to + # change their passwords. + config.reset_password_within = 2.hours + + # ==> Configuration for :encryptable + # Allow you to use another encryption algorithm besides bcrypt (default). You can use + # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, + # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) + # and :restful_authentication_sha1 (then you should set stretches to 10, and copy + # REST_AUTH_SITE_KEY to pepper) + # config.encryptor = :sha512 + + # ==> Configuration for :token_authenticatable + # Defines name of the authentication token params key + # config.token_authentication_key = :auth_token + + # If true, authentication through token does not store user in session and needs + # to be supplied on each request. Useful if you are using the token as API token. + # config.stateless_token = false + + # ==> Scopes configuration + # Turn scoped views on. Before rendering "sessions/new", it will first check for + # "users/sessions/new". It's turned off by default because it's slower if you + # are using only default views. + # config.scoped_views = false + + # Configure the default scope given to Warden. By default it's the first + # devise role declared in your routes (usually :user). + # config.default_scope = :user + + # Configure sign_out behavior. + # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope). + # The default is true, which means any logout action will sign out all active scopes. + # config.sign_out_all_scopes = true + + # ==> Navigation configuration + # Lists the formats that should be treated as navigational. Formats like + # :html, should redirect to the sign in page when the user does not have + # access, but formats like :xml or :json, should return 401. + # + # If you have any extra navigational formats, like :iphone or :mobile, you + # should add them to the navigational formats lists. + # + # The :"*/*" and "*/*" formats below is required to match Internet + # Explorer requests. + # config.navigational_formats = [:"*/*", "*/*", :html] + + # The default HTTP method used to sign out a resource. Default is :delete. + config.sign_out_via = :delete + + # ==> OmniAuth + # Add a new OmniAuth provider. Check the wiki for more information on setting + # up on your models and hooks. + # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' + + # ==> Warden configuration + # If you want to use other strategies, that are not supported by Devise, or + # change the failure app, you can configure them inside the config.warden block. + # + # config.warden do |manager| + # manager.failure_app = AnotherApp + # manager.intercept_401 = false + # manager.default_strategies(:scope => :user).unshift :some_external_strategy + # end +end diff --git a/spec/fake_app.rb b/spec/fake_app.rb new file mode 100644 index 0000000..f7db07f --- /dev/null +++ b/spec/fake_app.rb @@ -0,0 +1,97 @@ +require 'active_record' +require 'action_controller/railtie' +require 'action_view/railtie' + +require "cancan" +require "cancan/ability" +require "cancan/controller_resource" +require "cancan/controller_additions" + +require 'devise' +require 'devise/orm/active_record' + +# database +ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}} +ActiveRecord::Base.establish_connection('test') + +# config +app = Class.new(Rails::Application) +app.config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" +app.config.session_store :cookie_store, :key => "_myapp_session" +app.config.active_support.deprecation = :log +app.initialize! + +require 'devise_config' + +# models +class User < ActiveRecord::Base + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :trackable, :validatable + attr_accessible :email, :password, :password_confirmation, :remember_me, :name + validates :name, :presence => true, :uniqueness => true + has_inboxes +end + +# routes +app.routes.draw do + devise_for :users +end + +#migrations +ActiveRecord::Base.silence do + ActiveRecord::Migration.verbose = false + ActiveRecord::Schema.define :version => 0 do + create_table "users", :force => true do |t| + t.string "email", :default => "", :null => false + t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.integer "sign_in_count", :default => 0 + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.datetime "created_at" + t.datetime "updated_at" + t.string "name" + end + + create_table "discussions", :force => true do |t| + t.integer "user_id" + t.integer "messages_count", :default => 0 + t.boolean "is_private", :default => true + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "messages", :force => true do |t| + t.integer "user_id" + t.integer "discussion_id" + t.text "body" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "speakers", :force => true do |t| + t.integer "user_id" + t.integer "discussion_id" + t.datetime "created_at" + t.datetime "updated_at" + end + end +end + +# controllers +class ApplicationController < ActionController::Base + before_filter :assign_unread_discussions + + private + + def assign_unread_discussions + @unread_discussions_count = Discussion.unread_for(current_user).count if user_signed_in? + end +end + +# helpers +Object.const_set(:ApplicationHelper, Module.new)
\ No newline at end of file diff --git a/spec/fake_gem.rb b/spec/fake_gem.rb new file mode 100644 index 0000000..4435a70 --- /dev/null +++ b/spec/fake_gem.rb @@ -0,0 +1,6 @@ +# Simulate a gem providing a subclass of ActiveRecord::Base before the Railtie is loaded. + +require 'active_record' + +class GemDefinedModel < ActiveRecord::Base +end
\ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 174ab57..5caa0f5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,2 +1,29 @@ +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +$LOAD_PATH.unshift(File.dirname(__FILE__)) +require 'rails' +require 'active_record' require 'inboxes' +# require 'database_cleaner' +# Ensure we use 'syck' instead of 'psych' in 1.9.2 +# RubyGems >= 1.5.0 uses 'psych' on 1.9.2, but +# Psych does not yet support YAML 1.1 merge keys. +# Merge keys is often used in mongoid.yml +# See: http://redmine.ruby-lang.org/issues/show/4300 +if RUBY_VERSION >= '1.9.2' + YAML::ENGINE.yamler = 'syck' +end +# require 'fake_gem' +require 'fake_app' +require 'rspec/rails' +# Requires supporting files with custom matchers and macros, etc, +# in ./support/ and its subdirectories. +Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} + +# RSpec.configure do |config| +# config.mock_with :rr +# config.before :all do +# # ActiveRecord::Base.connection.execute 'CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255))' unless ActiveRecord::Base.connection.table_exists? 'users' +# # CreateAllTables.up unless ActiveRecord::Base.connection.table_exists? 'users' +# end +# end
\ No newline at end of file diff --git a/spec/support/factories.rb b/spec/support/factories.rb deleted file mode 100644 index a7eaad6..0000000 --- a/spec/support/factories.rb +++ /dev/null @@ -1,25 +0,0 @@ -FactoryGirl.define do - - factory :user do - email {Factory.next(:email)} - first_name 'user' - last_name 'usered' - username {Factory.next(:login)} - password "foobar" - password_confirmation { |u| u.password } - role 2 - end - - factory :discussion do - recipient_ids {[Factory(:user).id, Factory(:user).id]} - end - - factory :message do - association :user - association :discussion - # user {Factory(:user)} - # discussion {Factory(:discussion)} - end - - -end
\ No newline at end of file |
