flexible_feeds 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/javascripts/flexible_feeds/application.js +13 -0
  6. data/app/assets/stylesheets/flexible_feeds/application.css +13 -0
  7. data/app/controllers/flexible_feeds/application_controller.rb +4 -0
  8. data/app/helpers/flexible_feeds/application_helper.rb +4 -0
  9. data/app/models/flexible_feeds/event.rb +59 -0
  10. data/app/models/flexible_feeds/event_join.rb +10 -0
  11. data/app/models/flexible_feeds/feed.rb +31 -0
  12. data/app/models/flexible_feeds/follow.rb +18 -0
  13. data/app/models/flexible_feeds/moderator_join.rb +18 -0
  14. data/app/models/flexible_feeds/vote.rb +32 -0
  15. data/app/views/layouts/flexible_feeds/application.html.erb +14 -0
  16. data/config/locales/en.yml +12 -0
  17. data/config/routes.rb +2 -0
  18. data/db/migrate/20140227194731_create_flexible_feeds_feeds.rb +16 -0
  19. data/db/migrate/20140227194816_create_flexible_feeds_events.rb +43 -0
  20. data/db/migrate/20140227194904_create_flexible_feeds_event_joins.rb +15 -0
  21. data/db/migrate/20140227195044_create_flexible_feeds_moderator_joins.rb +16 -0
  22. data/db/migrate/20140227195107_create_flexible_feeds_follows.rb +16 -0
  23. data/db/migrate/20140227195140_create_flexible_feeds_votes.rb +17 -0
  24. data/lib/flexible_feeds.rb +11 -0
  25. data/lib/flexible_feeds/acts_as_child.rb +54 -0
  26. data/lib/flexible_feeds/acts_as_eventable.rb +59 -0
  27. data/lib/flexible_feeds/acts_as_feedable.rb +32 -0
  28. data/lib/flexible_feeds/acts_as_follower.rb +38 -0
  29. data/lib/flexible_feeds/acts_as_moderator.rb +35 -0
  30. data/lib/flexible_feeds/acts_as_parent.rb +59 -0
  31. data/lib/flexible_feeds/engine.rb +10 -0
  32. data/lib/flexible_feeds/polymorphic_join.rb +35 -0
  33. data/lib/flexible_feeds/version.rb +3 -0
  34. data/lib/tasks/flexible_feeds_tasks.rake +4 -0
  35. metadata +203 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 38f307f3b75cd9a484848e2268b3cbf08c99dac3
4
+ data.tar.gz: 8bd9504bf1642e5e597e1f3a8ecd371f35e1da2f
5
+ SHA512:
6
+ metadata.gz: f7ae27f7602d9fb8af8e59a78baa6f536b4227444f6e50ed2b678075b4c5084546f1553517fc0a01ab3e90081f041fa6823323eab1e8b6e759495097386f46e7
7
+ data.tar.gz: c8d6a4393750c7eaa9a08c37048706f6aa621f3e8a89ca51ea794ff41198957461d6f6a40e3cf42e3c5f79d40eb2c50c85afbe370db5ae12a463c22036114e4b
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 Timothy Baron
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = FlexibleFeeds
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rdoc/task'
2
+
3
+ RDoc::Task.new(:rdoc) do |rdoc|
4
+ rdoc.rdoc_dir = 'rdoc'
5
+ rdoc.title = 'FlexibleFeeds'
6
+ rdoc.options << '--line-numbers'
7
+ rdoc.rdoc_files.include('README.rdoc')
8
+ rdoc.rdoc_files.include('lib/**/*.rb')
9
+ end
10
+
11
+ Bundler::GemHelper.install_tasks
12
+
13
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
14
+ load 'rails/tasks/engine.rake'
15
+ require "rspec/core/rake_task"
16
+
17
+ task :default => :spec
18
+
19
+ RSpec::Core::RakeTask.new(:spec) do |spec|
20
+ spec.pattern = 'spec/**/*_spec.rb'
21
+ # spec.rspec_opts = ['-cfs --backtrace']
22
+ end
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ module FlexibleFeeds
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module FlexibleFeeds
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,59 @@
1
+ module FlexibleFeeds
2
+ class Event < ActiveRecord::Base
3
+ belongs_to :ancestor, polymorphic: true
4
+ belongs_to :eventable, polymorphic: true
5
+ belongs_to :parent, polymorphic: true
6
+ belongs_to :creator, polymorphic: true
7
+
8
+ has_many :event_joins, dependent: :destroy
9
+ has_many :feeds, through: :event_joins
10
+ has_many :votes
11
+
12
+ validates :children_count, presence: true,
13
+ numericality: { only_integer: true, greater_than_or_equal_to: 0 }
14
+ validates :controversy, presence: true
15
+ validates :eventable, presence: true
16
+ validates :votes_sum, presence: true
17
+ validates :popularity, presence: true
18
+
19
+ scope :newest, -> { order("updated_at DESC") }
20
+ scope :oldest, -> { order("updated_at ASC") }
21
+ scope :loudest, -> { order("children_count DESC") }
22
+ scope :quietest, -> { order("children_count ASC") }
23
+ scope :simple_popular, -> { order("votes_sum DESC") }
24
+ scope :simple_unpopular, -> { order("votes_sum ASC") }
25
+ scope :popular, -> { order("popularity DESC") }
26
+ scope :unpopular, -> { order("popularity ASC") }
27
+ scope :controversial, -> { order("controversy DESC") }
28
+ scope :uncontroversial, -> { order("controversy ASC") }
29
+
30
+ def cast_vote(params)
31
+ Vote.cast_vote(params.merge({event: self}))
32
+ end
33
+
34
+ def calculate_stats
35
+ self.votes_for = votes.where(value: 1).count
36
+ self.votes_against = votes.where(value: -1).count
37
+ self.votes_sum = votes_for - votes_against
38
+ self.controversy = calculate_controversy(votes_for, votes_against)
39
+ self.popularity = calculate_popularity(votes_for,
40
+ votes_for + votes_against)
41
+ save
42
+ end
43
+
44
+ private
45
+ def calculate_controversy(pos, neg)
46
+ return 0 if pos == 0 || neg == 0
47
+ 100.0 * (pos > neg ? neg.to_f / pos.to_f : pos.to_f / neg.to_f)
48
+ end
49
+
50
+ # Thanks to Evan Miller
51
+ # http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
52
+ def calculate_popularity(pos, n)
53
+ return 0 if n == 0
54
+ z = 1.96
55
+ phat = 1.0*pos/n
56
+ (phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,10 @@
1
+ module FlexibleFeeds
2
+ class EventJoin < ActiveRecord::Base
3
+ belongs_to :event
4
+ belongs_to :feed
5
+
6
+ validates :event, presence: true
7
+ validates :feed, presence: true
8
+ validates :sticky, inclusion: { in: [true, false] }
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ module FlexibleFeeds
2
+ class Feed < ActiveRecord::Base
3
+ include FlexibleFeeds::PolymorphicJoin
4
+
5
+ belongs_to :feedable, polymorphic: true
6
+
7
+ has_many :event_joins, dependent: :destroy
8
+ has_many :events, through: :event_joins
9
+ has_many :follows, dependent: :destroy
10
+ has_many :moderator_joins, dependent: :destroy
11
+
12
+ polymorphically_joined_through :moderator_joins,
13
+ association_name: :moderators,
14
+ singular_association_name: :moderator
15
+
16
+ polymorphically_joined_through :follows,
17
+ association_name: :followers,
18
+ singular_association_name: :follower
19
+
20
+ validate :unique_name_per_feedable
21
+
22
+ private
23
+ def unique_name_per_feedable
24
+ if feedable.present? && feedable.respond_to?(:feeds) &&
25
+ feedable.feed_named(self.name).present?
26
+ errors[:base] << I18n.t("activerecord.errors.models.feed.attributes.name.not_unique")
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ module FlexibleFeeds
2
+ class Follow < ActiveRecord::Base
3
+ belongs_to :follower, polymorphic: true
4
+ belongs_to :feed
5
+
6
+ validates :follower, presence: true
7
+ validates :feed, presence: true
8
+ validate :the_join_table_is_unique
9
+
10
+ private
11
+ def the_join_table_is_unique
12
+ if FlexibleFeeds::Follow.where(feed: self.feed, follower: self.follower).
13
+ exists?
14
+ errors[:base] << I18n.t("activerecord.errors.models.follow.not_unique")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module FlexibleFeeds
2
+ class ModeratorJoin < ActiveRecord::Base
3
+ belongs_to :feed
4
+ belongs_to :moderator, polymorphic: true
5
+
6
+ validates :feed, presence: true
7
+ validates :moderator, presence: true
8
+ validate :the_join_table_is_unique
9
+
10
+ private
11
+ def the_join_table_is_unique
12
+ if FlexibleFeeds::ModeratorJoin.where(feed: self.feed,
13
+ moderator: self.moderator).exists?
14
+ errors[:base] << I18n.t("activerecord.errors.models.moderator_join.not_unique")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module FlexibleFeeds
2
+ class Vote < ActiveRecord::Base
3
+ belongs_to :event
4
+ belongs_to :voter, polymorphic: true
5
+
6
+ validates :value, inclusion: { in: [-1, 1] }, presence: true
7
+ validates :event, presence: true
8
+ validates :voter, presence: true
9
+
10
+ after_save :calculate_event_stats
11
+ after_destroy :calculate_event_stats
12
+
13
+ def self.cast_vote(params)
14
+ vote = find_by(voter: params[:voter], event: params[:event])
15
+ return create(params) if vote.nil?
16
+ vote.toggle_by_value(params[:value])
17
+ end
18
+
19
+ def toggle_by_value(submitted_value)
20
+ if value == submitted_value
21
+ destroy
22
+ else
23
+ update_attributes(value: submitted_value)
24
+ end
25
+ end
26
+
27
+ private
28
+ def calculate_event_stats
29
+ event.calculate_stats
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>FlexibleFeeds</title>
5
+ <%= stylesheet_link_tag "flexible_feeds/application", media: "all" %>
6
+ <%= javascript_include_tag "flexible_feeds/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,12 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ models:
5
+ feed:
6
+ attributes:
7
+ name:
8
+ not_unique: "already used within the scope of this feedable"
9
+ follow:
10
+ not_unique: "Already following."
11
+ moderator_join:
12
+ not_unique: "Already moderating."
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ FlexibleFeeds::Engine.routes.draw do
2
+ end
@@ -0,0 +1,16 @@
1
+ class CreateFlexibleFeedsFeeds < ActiveRecord::Migration
2
+ def change
3
+ create_table :flexible_feeds_feeds do |t|
4
+ t.string :feedable_type
5
+ t.integer :feedable_id
6
+ t.string :name
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :flexible_feeds_feeds, :name
12
+ add_index :flexible_feeds_feeds,
13
+ [:feedable_id, :feedable_type],
14
+ name: "flexible_feeds_feeds_on_parent"
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ class CreateFlexibleFeedsEvents < ActiveRecord::Migration
2
+ def change
3
+ create_table :flexible_feeds_events do |t|
4
+ t.string :eventable_type
5
+ t.integer :eventable_id
6
+ t.integer :creator_id
7
+ t.string :creator_type
8
+
9
+ t.integer :parent_id
10
+ t.string :parent_type
11
+ t.integer :ancestor_id
12
+ t.string :ancestor_type
13
+ t.integer :children_count, null: false, default: 0
14
+
15
+ t.integer :votes_sum, null: false, default: 0
16
+ t.integer :votes_for, null: false, default: 0
17
+ t.integer :votes_against, null: false, default: 0
18
+ t.float :controversy, null: false, default: 0, precision: 5, scale: 2
19
+ t.float :popularity, null: false, default: 0, precision: 5, scale: 2
20
+
21
+ t.timestamps
22
+ end
23
+
24
+ add_index :flexible_feeds_events, :votes_sum
25
+ add_index :flexible_feeds_events, :votes_for
26
+ add_index :flexible_feeds_events, :votes_against
27
+ add_index :flexible_feeds_events, :controversy
28
+ add_index :flexible_feeds_events, :popularity
29
+ add_index :flexible_feeds_events, :children_count
30
+ add_index :flexible_feeds_events,
31
+ [:eventable_id, :eventable_type],
32
+ name: "flexible_feeds_events_on_eventable"
33
+ add_index :flexible_feeds_events,
34
+ [:creator_id, :creator_type],
35
+ name: "flexible_feeds_events_on_creator"
36
+ add_index :flexible_feeds_events,
37
+ [:parent_id, :parent_type],
38
+ name: "flexible_feeds_events_on_parent"
39
+ add_index :flexible_feeds_events,
40
+ [:ancestor_id, :ancestor_type],
41
+ name: "flexible_feeds_events_on_ancestor"
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ class CreateFlexibleFeedsEventJoins < ActiveRecord::Migration
2
+ def change
3
+ create_table :flexible_feeds_event_joins do |t|
4
+ t.integer :feed_id
5
+ t.integer :event_id
6
+ t.boolean :sticky, null: false, default: false
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :flexible_feeds_event_joins, :sticky
12
+ add_index :flexible_feeds_event_joins, :feed_id
13
+ add_index :flexible_feeds_event_joins, :event_id
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class CreateFlexibleFeedsModeratorJoins < ActiveRecord::Migration
2
+ def change
3
+ create_table :flexible_feeds_moderator_joins do |t|
4
+ t.integer :feed_id
5
+ t.integer :moderator_id
6
+ t.string :moderator_type
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :flexible_feeds_moderator_joins, :feed_id
12
+ add_index :flexible_feeds_moderator_joins,
13
+ [:moderator_id, :moderator_type],
14
+ name: "flexible_feeds_moderator_joins_on_moderator"
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ class CreateFlexibleFeedsFollows < ActiveRecord::Migration
2
+ def change
3
+ create_table :flexible_feeds_follows do |t|
4
+ t.integer :feed_id
5
+ t.integer :follower_id
6
+ t.string :follower_type
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :flexible_feeds_follows, :feed_id
12
+ add_index :flexible_feeds_follows,
13
+ [:follower_id, :follower_type],
14
+ name: "flexible_feeds_follows_on_follower"
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ class CreateFlexibleFeedsVotes < ActiveRecord::Migration
2
+ def change
3
+ create_table :flexible_feeds_votes do |t|
4
+ t.integer :event_id
5
+ t.integer :voter_id
6
+ t.string :voter_type
7
+ t.integer :value, null: false, default: 0
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :flexible_feeds_votes, :event_id
13
+ add_index :flexible_feeds_votes,
14
+ [:voter_id, :voter_type],
15
+ name: "flexible_feeds_votes_on_voter"
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ require "flexible_feeds/engine"
2
+ require "flexible_feeds/acts_as_feedable"
3
+ require "flexible_feeds/acts_as_eventable"
4
+ require "flexible_feeds/acts_as_moderator"
5
+ require "flexible_feeds/acts_as_follower"
6
+ require "flexible_feeds/acts_as_parent"
7
+ require "flexible_feeds/acts_as_child"
8
+ require "flexible_feeds/polymorphic_join"
9
+
10
+ module FlexibleFeeds
11
+ end
@@ -0,0 +1,54 @@
1
+ module FlexibleFeeds
2
+ module ActsAsChild
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_child(options = {})
7
+
8
+ def is_childish?
9
+ true
10
+ end
11
+
12
+ send :include, InstanceMethods
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+ def is_childish?
18
+ true
19
+ end
20
+
21
+ def parent
22
+ event.parent
23
+ end
24
+
25
+ def ancestor
26
+ event.ancestor
27
+ end
28
+
29
+ def child_of(parent)
30
+ if can_accept_parent?(parent)
31
+ ancestor = parent.try(:ancestor) || parent.event
32
+ event.update_attributes(parent: parent.event, ancestor: ancestor)
33
+ end
34
+ end
35
+
36
+ private
37
+ def can_accept_parent?(parent)
38
+ if parent.class.try(:permitted_children).present?
39
+ parent.class.permitted_children.include?(self.class)
40
+ elsif parent.class.try(:unpermitted_children).present?
41
+ !parent.class.unpermitted_children.include?(self.class)
42
+ else
43
+ parent.try(:is_parental?)
44
+ end
45
+ end
46
+
47
+ def initialize_event
48
+ event || create_event
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ ActiveRecord::Base.send :include, FlexibleFeeds::ActsAsChild
@@ -0,0 +1,59 @@
1
+ module FlexibleFeeds
2
+ module ActsAsEventable
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_eventable(options = {})
7
+
8
+ has_one :event, as: :eventable, class_name: "FlexibleFeeds::Event",
9
+ dependent: :destroy
10
+ has_many :feeds, through: :event, class_name: "FlexibleFeeds::Feed"
11
+
12
+ after_save :post_to_default_feeds
13
+
14
+ send :include, InstanceMethods
15
+
16
+ cattr_accessor :default_feeds
17
+ self.default_feeds = options[:add_to_feeds] || :default_custom_feeds
18
+
19
+ cattr_accessor :created_by
20
+ self.created_by = options[:created_by]
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ def post_to_feeds(*destinations)
26
+ destinations = destinations[0] if destinations[0].kind_of?(Array)
27
+ self.class.transaction do
28
+ create_event_for(destinations)
29
+ end
30
+ end
31
+
32
+ def touch_event
33
+ event.touch
34
+ end
35
+
36
+ def default_custom_feeds
37
+ []
38
+ end
39
+
40
+ private
41
+ def creator
42
+ send(created_by) if created_by.present?
43
+ end
44
+
45
+ def post_to_default_feeds
46
+ post_to_feeds(public_send(default_feeds))
47
+ end
48
+
49
+ def create_event_for(destinations)
50
+ event = event || create_event!(creator: creator)
51
+ destinations.each do |feed|
52
+ event.event_joins.create!(feed: feed) if feed.present?
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ ActiveRecord::Base.send :include, FlexibleFeeds::ActsAsEventable
@@ -0,0 +1,32 @@
1
+ module FlexibleFeeds
2
+ module ActsAsFeedable
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_feedable(options = {})
7
+
8
+ cattr_accessor :has_many_feeds
9
+ self.has_many_feeds = options[:has_many] || false
10
+
11
+ if has_many_feeds == true
12
+ has_many :feeds, as: :feedable, class_name: "FlexibleFeeds::Feed",
13
+ dependent: :destroy
14
+ else
15
+ has_one :feed, as: :feedable, class_name: "FlexibleFeeds::Feed",
16
+ dependent: :destroy
17
+ after_create :create_feed
18
+ end
19
+
20
+ send :include, InstanceMethods
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ def feed_named(name)
26
+ feeds.find_by(name: name)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ ActiveRecord::Base.send :include, FlexibleFeeds::ActsAsFeedable
@@ -0,0 +1,38 @@
1
+ module FlexibleFeeds
2
+ module ActsAsFollower
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_follower
7
+
8
+ has_many :follows, as: :follower, class_name: "FlexibleFeeds::Follow",
9
+ dependent: :destroy
10
+ has_many :followed_feeds, through: :follows, source: :feed,
11
+ class_name: "FlexibleFeeds::Feed"
12
+ has_many :aggregate_follows, -> { group 'flexible_feeds_events.id' },
13
+ through: :followed_feeds, source: :events,
14
+ class_name: "FlexibleFeeds::Event"
15
+
16
+ send :include, InstanceMethods
17
+ end
18
+ end
19
+
20
+ module InstanceMethods
21
+ def follow(feed)
22
+ follows.create(feed: feed)
23
+ end
24
+
25
+ def unfollow(feed)
26
+ join = follows.find_by(feed: feed)
27
+ return false if join.nil?
28
+ join.destroy
29
+ end
30
+
31
+ def is_following?(feed)
32
+ followed_feeds.include?(feed)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ ActiveRecord::Base.send :include, FlexibleFeeds::ActsAsFollower
@@ -0,0 +1,35 @@
1
+ module FlexibleFeeds
2
+ module ActsAsModerator
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_moderator
7
+
8
+ has_many :moderator_joins, as: :moderator,
9
+ class_name: "FlexibleFeeds::ModeratorJoin", dependent: :destroy
10
+ has_many :moderated_feeds, through: :moderator_joins, source: :feed,
11
+ class_name: "FlexibleFeeds::Feed"
12
+
13
+ send :include, InstanceMethods
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def moderate(feed)
19
+ moderator_joins.create(feed: feed)
20
+ end
21
+
22
+ def unmoderate(feed)
23
+ join = moderator_joins.find_by(feed: feed)
24
+ return false if join.nil?
25
+ join.destroy
26
+ end
27
+
28
+ def is_moderating?(feed)
29
+ moderated_feeds.include?(feed)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ ActiveRecord::Base.send :include, FlexibleFeeds::ActsAsModerator
@@ -0,0 +1,59 @@
1
+ module FlexibleFeeds
2
+ module ActsAsParent
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_parent(options = {})
7
+
8
+ cattr_accessor :permitted_children
9
+ cattr_accessor :unpermitted_children
10
+ self.permitted_children = options[:permitted_children]
11
+ self.unpermitted_children = options[:unpermitted_children]
12
+
13
+ def is_parental?
14
+ true
15
+ end
16
+
17
+ send :include, InstanceMethods
18
+ end
19
+ end
20
+
21
+ module InstanceMethods
22
+ def is_parental?
23
+ true
24
+ end
25
+
26
+ def children
27
+ FlexibleFeeds::Event.where(parent: event).includes(:eventable)
28
+ end
29
+
30
+ def descendants
31
+ FlexibleFeeds::Event.where(ancestor: event).includes(:eventable)
32
+ end
33
+
34
+ def parent_of(child)
35
+ if can_accept_child?(child)
36
+ ancestor = self.try(:ancestor) || self.event
37
+ child.event.update_attributes(parent: self.event, ancestor: ancestor)
38
+ end
39
+ end
40
+
41
+ private
42
+ def can_accept_child?(child)
43
+ if self.class.permitted_children.present?
44
+ self.class.permitted_children.include?(child.class)
45
+ elsif self.class.unpermitted_children.present?
46
+ !self.class.unpermitted_children.include?(child.class)
47
+ else
48
+ child.try(:is_childish?)
49
+ end
50
+ end
51
+
52
+ def initialize_event
53
+ event || create_event
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ ActiveRecord::Base.send :include, FlexibleFeeds::ActsAsParent
@@ -0,0 +1,10 @@
1
+ module FlexibleFeeds
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace FlexibleFeeds
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module FlexibleFeeds
2
+ module PolymorphicJoin
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def polymorphically_joined_through(join_table, params)
7
+ association = params[:association_name]
8
+ singular_association = params[:singular_association_name]
9
+
10
+ define_method association do
11
+ send(join_table).collect { |join| join.send(singular_association) }
12
+ end
13
+
14
+ define_method "#{association}_include?".to_sym do |instance|
15
+ send(join_table).where(singular_association => instance).exists?
16
+ end
17
+
18
+ define_method "add_#{singular_association}".to_sym do |instance|
19
+ result = send(join_table).create(singular_association => instance)
20
+ self.reload()
21
+ result
22
+ end
23
+
24
+ define_method "remove_#{singular_association}".to_sym do |instance|
25
+ return false unless send("#{association}_include?", instance)
26
+ result = send(join_table).
27
+ find_by(singular_association => instance).destroy
28
+ self.reload()
29
+ result
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,3 @@
1
+ module FlexibleFeeds
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :flexible_feeds do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,203 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flexible_feeds
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - timothycommoner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: debugger
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-spork
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: factory_girl_rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: database_cleaner
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: shoulda
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: A flexible feed system, allowing for everything from automated events
140
+ to nested user-generated discussions.
141
+ email:
142
+ - timothythehuman@gmail.com
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - app/helpers/flexible_feeds/application_helper.rb
148
+ - app/models/flexible_feeds/moderator_join.rb
149
+ - app/models/flexible_feeds/vote.rb
150
+ - app/models/flexible_feeds/event.rb
151
+ - app/models/flexible_feeds/event_join.rb
152
+ - app/models/flexible_feeds/feed.rb
153
+ - app/models/flexible_feeds/follow.rb
154
+ - app/controllers/flexible_feeds/application_controller.rb
155
+ - app/assets/javascripts/flexible_feeds/application.js
156
+ - app/assets/stylesheets/flexible_feeds/application.css
157
+ - app/views/layouts/flexible_feeds/application.html.erb
158
+ - config/routes.rb
159
+ - config/locales/en.yml
160
+ - db/migrate/20140227195107_create_flexible_feeds_follows.rb
161
+ - db/migrate/20140227194816_create_flexible_feeds_events.rb
162
+ - db/migrate/20140227195044_create_flexible_feeds_moderator_joins.rb
163
+ - db/migrate/20140227194904_create_flexible_feeds_event_joins.rb
164
+ - db/migrate/20140227195140_create_flexible_feeds_votes.rb
165
+ - db/migrate/20140227194731_create_flexible_feeds_feeds.rb
166
+ - lib/tasks/flexible_feeds_tasks.rake
167
+ - lib/flexible_feeds/acts_as_eventable.rb
168
+ - lib/flexible_feeds/acts_as_moderator.rb
169
+ - lib/flexible_feeds/acts_as_follower.rb
170
+ - lib/flexible_feeds/polymorphic_join.rb
171
+ - lib/flexible_feeds/acts_as_feedable.rb
172
+ - lib/flexible_feeds/version.rb
173
+ - lib/flexible_feeds/acts_as_parent.rb
174
+ - lib/flexible_feeds/acts_as_child.rb
175
+ - lib/flexible_feeds/engine.rb
176
+ - lib/flexible_feeds.rb
177
+ - MIT-LICENSE
178
+ - Rakefile
179
+ - README.rdoc
180
+ homepage: https://github.com/timothycommoner/flexible_feeds
181
+ licenses: []
182
+ metadata: {}
183
+ post_install_message:
184
+ rdoc_options: []
185
+ require_paths:
186
+ - lib
187
+ required_ruby_version: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - '>='
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ required_rubygems_version: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - '>='
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ requirements: []
198
+ rubyforge_project:
199
+ rubygems_version: 2.0.5
200
+ signing_key:
201
+ specification_version: 4
202
+ summary: A flexible feed system.
203
+ test_files: []