flexible_feeds 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []