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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +22 -0
- data/app/assets/javascripts/flexible_feeds/application.js +13 -0
- data/app/assets/stylesheets/flexible_feeds/application.css +13 -0
- data/app/controllers/flexible_feeds/application_controller.rb +4 -0
- data/app/helpers/flexible_feeds/application_helper.rb +4 -0
- data/app/models/flexible_feeds/event.rb +59 -0
- data/app/models/flexible_feeds/event_join.rb +10 -0
- data/app/models/flexible_feeds/feed.rb +31 -0
- data/app/models/flexible_feeds/follow.rb +18 -0
- data/app/models/flexible_feeds/moderator_join.rb +18 -0
- data/app/models/flexible_feeds/vote.rb +32 -0
- data/app/views/layouts/flexible_feeds/application.html.erb +14 -0
- data/config/locales/en.yml +12 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20140227194731_create_flexible_feeds_feeds.rb +16 -0
- data/db/migrate/20140227194816_create_flexible_feeds_events.rb +43 -0
- data/db/migrate/20140227194904_create_flexible_feeds_event_joins.rb +15 -0
- data/db/migrate/20140227195044_create_flexible_feeds_moderator_joins.rb +16 -0
- data/db/migrate/20140227195107_create_flexible_feeds_follows.rb +16 -0
- data/db/migrate/20140227195140_create_flexible_feeds_votes.rb +17 -0
- data/lib/flexible_feeds.rb +11 -0
- data/lib/flexible_feeds/acts_as_child.rb +54 -0
- data/lib/flexible_feeds/acts_as_eventable.rb +59 -0
- data/lib/flexible_feeds/acts_as_feedable.rb +32 -0
- data/lib/flexible_feeds/acts_as_follower.rb +38 -0
- data/lib/flexible_feeds/acts_as_moderator.rb +35 -0
- data/lib/flexible_feeds/acts_as_parent.rb +59 -0
- data/lib/flexible_feeds/engine.rb +10 -0
- data/lib/flexible_feeds/polymorphic_join.rb +35 -0
- data/lib/flexible_feeds/version.rb +3 -0
- data/lib/tasks/flexible_feeds_tasks.rake +4 -0
- 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
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,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,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>
|
data/config/routes.rb
ADDED
@@ -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,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
|
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: []
|