feeder 0.3.0 → 0.5.1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +119 -2
  3. data/app/controllers/feeder/application_controller.rb +1 -1
  4. data/app/controllers/feeder/items_controller.rb +7 -0
  5. data/app/models/feeder/feedable_observer.rb +43 -7
  6. data/app/models/feeder/item.rb +2 -6
  7. data/app/views/feeder/items/_item.html.erb +1 -1
  8. data/app/views/feeder/items/_item.json.jbuilder +1 -0
  9. data/app/views/feeder/{feeds → items}/index.html.erb +1 -1
  10. data/app/views/feeder/items/index.json.jbuilder +1 -0
  11. data/config/routes.rb +1 -1
  12. data/db/migrate/20140321085409_add_sticky_to_feeder_items.rb +5 -0
  13. data/db/migrate/20140401131911_prohibit_null_on_feeder_item_stickies.rb +13 -0
  14. data/db/migrate/20140526110451_add_blocked_to_feeder_items.rb +5 -0
  15. data/db/migrate/20140526110501_add_reported_to_feeder_items.rb +5 -0
  16. data/lib/feeder.rb +18 -2
  17. data/lib/feeder/active_record.rb +9 -0
  18. data/lib/feeder/concerns.rb +6 -0
  19. data/lib/feeder/concerns/controllers.rb +5 -0
  20. data/lib/feeder/concerns/controllers/items_controller.rb +22 -0
  21. data/lib/feeder/concerns/feedable.rb +27 -0
  22. data/lib/feeder/concerns/helpers.rb +5 -0
  23. data/lib/feeder/concerns/helpers/filter.rb +20 -0
  24. data/lib/feeder/concerns/models.rb +5 -0
  25. data/lib/feeder/concerns/models/item.rb +31 -0
  26. data/lib/feeder/configuration.rb +19 -5
  27. data/lib/feeder/engine.rb +3 -2
  28. data/lib/feeder/version.rb +1 -1
  29. data/spec/controllers/feeder/items_controller_spec.rb +36 -0
  30. data/spec/dummy/app/models/message.rb +2 -0
  31. data/spec/dummy/config/initializers/feeder.rb +1 -1
  32. data/spec/dummy/db/development.sqlite3 +0 -0
  33. data/spec/dummy/db/schema.rb +4 -1
  34. data/spec/dummy/db/test.sqlite3 +0 -0
  35. data/spec/dummy/log/development.log +359 -0
  36. data/spec/dummy/log/test.log +53050 -0
  37. data/spec/factories/feeder/items.rb +10 -0
  38. data/spec/lib/feeder/concerns/feedable_spec.rb +33 -0
  39. data/spec/lib/feeder/configuration_spec.rb +6 -6
  40. data/spec/lib/feeder_spec.rb +14 -2
  41. data/spec/models/feeder/feedable_observer_spec.rb +95 -0
  42. data/spec/models/feeder/item_spec.rb +38 -0
  43. data/spec/spec_helper.rb +4 -0
  44. data/spec/support/shared_examples/filterable.rb +48 -0
  45. metadata +97 -39
  46. data/app/controllers/feeder/feeds_controller.rb +0 -9
  47. data/app/views/layouts/feeder/application.html.erb +0 -14
  48. data/spec/controllers/feeder/feeds_controller_spec.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22e1b2d7d889186ead64cdeda3bda95f03db5608
4
- data.tar.gz: f4fcc534375a8010d0175406332ce0fbdf9fa34d
3
+ metadata.gz: 04e9d95c32a3be20252ca3365e9e3e9aa6d61599
4
+ data.tar.gz: d6765c13e947c4a5d30b6f30125b9afcbfc79106
5
5
  SHA512:
6
- metadata.gz: 3b008831dc5207f16c7bf28bc3b6b5103de2c74d4bbd385a823ff5b44bfa8b93641b6bc298dff967eca4fae94f1ff59bf51799fb2f7f05a4ae79a59108519763
7
- data.tar.gz: 42b34bff84ff71b0451fd4871c6aa15370d1d6a8c3068bcbec789e8c872dfb57f9766c98e646198404a13b952c85586ce9ead7c0b5c33249b1fb3db1a2c626fb
6
+ metadata.gz: 7bc92c030d928c6642485ca8dd2ecb2c74866bd5a630c3589bdd2d9aa50141cecf0ca0c71f98fef0a9d441a1ffa02d9d81fb45f4c348af385491b21621eee572
7
+ data.tar.gz: 527433e9fb8feae052f6b99e192d10064676bb303a3b69fda212ba1523e58d3f94b0c3f3c31e2251524119f68ef56d5d83c4cb0aaf5e21568b7524a3ae29b3e6
data/README.md CHANGED
@@ -1,4 +1,10 @@
1
- # Mingle
1
+ # Feeder
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/feeder.svg)](https://rubygems.org/gems/feeder)
4
+ [![Build Status](https://img.shields.io/travis/hyperoslo/feeder.svg)](https://travis-ci.org/hyperoslo/feeder)
5
+ [![Dependency Status](https://img.shields.io/gemnasium/hyperoslo/feeder.svg)](https://gemnasium.com/hyperoslo/feeder)
6
+ [![Code Climate](https://img.shields.io/codeclimate/github/hyperoslo/feeder.svg)](https://codeclimate.com/github/hyperoslo/feeder)
7
+ [![Coverage Status](https://img.shields.io/coveralls/hyperoslo/feeder.svg)](https://coveralls.io/r/hyperoslo/feeder)
2
8
 
3
9
  Feeder gives you a mountable engine that provides a route to a feed page in your
4
10
  Ruby on Rails application.
@@ -20,11 +26,122 @@ Or install it yourself as:
20
26
  Install the migrations:
21
27
 
22
28
  rake feeder:install:migrations
23
-
29
+
24
30
  Run the migrations:
25
31
 
26
32
  rake db:migrate
27
33
 
34
+ ## Usage
35
+
36
+ To make Feeder available, mount it to a route by adding the following somewhere
37
+ in your _config/routes.rb_:
38
+
39
+ ```ruby
40
+ mount Feeder::Engine => "/feed"
41
+ ```
42
+
43
+ You will now be able to display a feed on _/feed_ in your application. In order
44
+ for Feeder to display anything in your feed, however, you will need to make
45
+ views per item type in the feed. Feeder looks up these views in
46
+ _app/views/feeder/types_ by default, and then checks for a partial with the same
47
+ name as your item type. As an example, if you have a `Message` model that you
48
+ wish to list out on your feed, you would make a file called *_message.html.erb*
49
+ in _app/views/feeder/types_.
50
+
51
+ Feeder also comes with an observer for automatically generating wrapper items
52
+ for your feedables (e.g. messages). In order to use it, you only need to register
53
+ `Feeder::FeedableObserver` into your app, which can be done in
54
+ _config/application.rb_ like this:
55
+
56
+ ```ruby
57
+ config.active_record.observers = [ 'Feeder::FeedableObserver' ]
58
+ ```
59
+
60
+ Then, all you need to do is tell Feeder what to
61
+ observe, which is done through an initializer, like this:
62
+
63
+ ```ruby
64
+ Feeder.configure do |config|
65
+ config.observe Message
66
+ end
67
+ ```
68
+
69
+ ... and declare that your `Message` model is feedable:
70
+
71
+ ```ruby
72
+ class Message < ActiveRecord::Base
73
+ feedable
74
+ end
75
+ ```
76
+
77
+ If you don't want to publish every message in the feed, you can supply a condition
78
+ to `observe`:
79
+
80
+ ```ruby
81
+ Feeder.configure do |config|
82
+ config.observe Message, if: -> message { message.show_in_feed? }
83
+ end
84
+ ```
85
+
86
+ Pretty neat.
87
+
88
+ ### Filtering
89
+
90
+ Want to filter out what feedables to display in your feed? We've got you covered
91
+ through the all-powerful `filter` scope! Give it a hash of feedables and the
92
+ IDs that you want to fetch, and Feeder makes sure to only return feed items with
93
+ the specified feedables. You may also pass in the symbol `:all` instead of a
94
+ list of IDs, which would fetch each of them. For example: say you have the
95
+ following feedables:
96
+
97
+ - `ShortMessage`
98
+ - `Tweet`
99
+ - `NewsArticle`
100
+
101
+ To get `Feeder::Item`s with news articles having IDs `[1, 2, 3, 4, 5]`, tweets
102
+ `[2, 4, 6, 7]` and all short message, you could do like this:
103
+
104
+ ```ruby
105
+ Feeder::Item.filter(
106
+ NewsArticle => [1, 2, 3, 4, 5],
107
+ Tweet => [2, 4, 6, 7],
108
+ ShortMessage => :all,
109
+ )
110
+ ```
111
+
112
+ **NOTE:** The `filter` scope is _exclusive_, meaning that anything you _do not_
113
+ pass in to it will also not be brought back. With the above feedables, if you
114
+ only want short messages `[1, 3, 4]`, but all of the tweets and news articles,
115
+ you would have to specify them as well, like this:
116
+
117
+ ```ruby
118
+ Feeder::Item.filter(
119
+ ShortMessage => [1, 3, 4],
120
+ Tweet => :all,
121
+ NewsArticle => :all
122
+ )
123
+ ```
124
+
125
+ The following would only return feed items with short messages:
126
+
127
+ ```ruby
128
+ Feeder::Item.filter(ShortMessage => [1, 3, 4])
129
+ ```
130
+
131
+ ### Configuration
132
+
133
+ ```ruby
134
+ Feeder.configure do |config|
135
+ # A list of scopes that will be applied to the feed items in the controller.
136
+ config.scopes << proc { limit 5 }
137
+ end
138
+ ```
139
+
140
+ ### Stickies
141
+
142
+ You can "sticky" messages in your feed so they're pinned at the top regardless of when
143
+ they were created. Just set the `sticky` attribute and Feeder will take care of the rest.
144
+
28
145
  ## Contributing
29
146
 
30
147
  1. Fork it
@@ -1,4 +1,4 @@
1
1
  module Feeder
2
- class ApplicationController < ActionController::Base
2
+ class ApplicationController < ::ApplicationController
3
3
  end
4
4
  end
@@ -0,0 +1,7 @@
1
+ require_dependency "feeder/application_controller"
2
+
3
+ module Feeder
4
+ class ItemsController < ApplicationController
5
+ include Feeder::Concerns::Controllers::ItemsController
6
+ end
7
+ end
@@ -1,13 +1,49 @@
1
1
  module Feeder
2
- class FeedableObserver < ActiveRecord::Observer
3
- observe Feeder.config.observables
2
+ class FeedableObserver < ::ActiveRecord::Observer
3
+ observe Feeder.config.observables.keys
4
4
 
5
5
  def after_create(feedable)
6
- Feeder::Item.create!(
7
- feedable: feedable,
8
- created_at: feedable.created_at,
9
- published_at: Time.zone.now
10
- )
6
+ options = options_for feedable
7
+
8
+ if condition = options[:if]
9
+ if condition.respond_to? :call
10
+ return unless condition.call feedable
11
+ else
12
+ return unless feedable.send condition
13
+ end
14
+ end
15
+
16
+ feedable.create_feeder_item! do |item|
17
+ item.feedable = feedable
18
+ item.created_at = feedable.created_at
19
+ item.published_at = Time.zone.now
20
+
21
+ if feedable.respond_to? :sticky
22
+ item.sticky = feedable.sticky
23
+ end
24
+ end
25
+ end
26
+
27
+ def after_save(feedable)
28
+ item = feedable.feeder_item
29
+
30
+ if item
31
+ if feedable.respond_to? :sticky
32
+ item.sticky = feedable.sticky
33
+ end
34
+
35
+ item.save!
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def options_for(feedable)
42
+ (observables[feedable.class] || observables[feedable.class.to_s]) or raise StandardError, "#{feedable} is not observed"
43
+ end
44
+
45
+ def observables
46
+ Feeder.config.observables
11
47
  end
12
48
  end
13
49
  end
@@ -1,9 +1,5 @@
1
1
  module Feeder
2
- class Item < ActiveRecord::Base
3
- belongs_to :feedable, polymorphic: true
4
-
5
- def type
6
- feedable_type.underscore
7
- end
2
+ class Item < ::ActiveRecord::Base
3
+ include Feeder::Concerns::Models::Item
8
4
  end
9
5
  end
@@ -1,3 +1,3 @@
1
- <div class="feed_item <%= item.type %>">
1
+ <div class="feed-item <%= item.type.parameterize %>">
2
2
  <%= render "feeder/types/#{item.type}", feedable: item.feedable %>
3
3
  </div>
@@ -0,0 +1 @@
1
+ json.partial! "feeder/types/#{item.type}", feedable: item.feedable
@@ -1,3 +1,3 @@
1
- <div class="feed_items">
1
+ <div class="feed-items">
2
2
  <%= render @items %>
3
3
  </div>
@@ -0,0 +1 @@
1
+ json.array! @items, partial: "feeder/items/item", as: :item
@@ -1,3 +1,3 @@
1
1
  Feeder::Engine.routes.draw do
2
- root to: 'feeds#index', via: :get
2
+ resources :items, path: '/(.:format)', only: :index
3
3
  end
@@ -0,0 +1,5 @@
1
+ class AddStickyToFeederItems < ActiveRecord::Migration
2
+ def change
3
+ add_column :feeder_items, :sticky, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ class ProhibitNullOnFeederItemStickies < ActiveRecord::Migration
2
+ class Feeder::Item < ActiveRecord::Base; end
3
+
4
+ def up
5
+ Feeder::Item.where(sticky: nil).update_all sticky: false
6
+
7
+ change_column :feeder_items, :sticky, :boolean, null: false
8
+ end
9
+
10
+ def down
11
+ change_column :feeder_items, :sticky, :boolean, null: true
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class AddBlockedToFeederItems < ActiveRecord::Migration
2
+ def change
3
+ add_column :feeder_items, :blocked, :boolean, null: false, default: false
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddReportedToFeederItems < ActiveRecord::Migration
2
+ def change
3
+ add_column :feeder_items, :reported, :boolean, null: false, default: false
4
+ end
5
+ end
@@ -1,5 +1,8 @@
1
1
  require "feeder/engine"
2
2
  require "feeder/configuration"
3
+ require "feeder/concerns"
4
+ require "feeder/active_record"
5
+ require "kaminari"
3
6
 
4
7
  module Feeder
5
8
 
@@ -9,9 +12,22 @@ module Feeder
9
12
  end
10
13
 
11
14
  def configure
12
- raise ArgumentError, "No block provided" unless block_given?
15
+ yield config if block_given?
16
+ end
17
+
18
+ # Set temporary configuration options for the duration of the given block.
19
+ #
20
+ # options - A Hash describing temporary configuration options.
21
+ def temporarily options = {}
22
+ original = @configuration.dup
23
+
24
+ options.each do |key, value|
25
+ @configuration.send "#{key}=", value
26
+ end
13
27
 
14
- yield config
28
+ yield
29
+ ensure
30
+ @configuration = original
15
31
  end
16
32
  end
17
33
 
@@ -0,0 +1,9 @@
1
+ module Feeder::ActiveRecord
2
+ module Extensions
3
+ def feedable
4
+ include Feeder::Concerns::Feedable
5
+ end
6
+ end
7
+ end
8
+
9
+ ::ActiveRecord::Base.send :extend, Feeder::ActiveRecord::Extensions if defined?(ActiveRecord)
@@ -0,0 +1,6 @@
1
+ module Feeder::Concerns
2
+ require 'feeder/concerns/controllers'
3
+ require 'feeder/concerns/helpers'
4
+ require 'feeder/concerns/feedable'
5
+ require 'feeder/concerns/models'
6
+ end
@@ -0,0 +1,5 @@
1
+ module Feeder
2
+ module Concerns::Controllers
3
+ require 'feeder/concerns/controllers/items_controller'
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ module Feeder
2
+ module Concerns::Controllers::ItemsController
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ respond_to :html, :json
7
+
8
+ def index
9
+ @items = Item.order(sticky: :desc)
10
+
11
+ Feeder.config.scopes.each do |scope|
12
+ @items = @items.instance_eval &scope
13
+ end
14
+
15
+ @items = @items.page(params[:page] || 1)
16
+ @items = @items.per(params[:limit] || 25)
17
+
18
+ respond_with @items
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ module Feeder::Concerns::Feedable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ attr_accessor :sticky
6
+
7
+ has_one :feeder_item, as: :feedable, class_name: 'Feeder::Item', dependent: :destroy
8
+
9
+ def sticky
10
+ if feeder_item
11
+ feeder_item.sticky
12
+ else
13
+ !!@sticky
14
+ end
15
+ end
16
+
17
+ def sticky= value
18
+ @sticky = value
19
+
20
+ if feeder_item
21
+ feeder_item.sticky = value
22
+ end
23
+ end
24
+
25
+ delegate :block, :unblock, :blocked?, :report, :unreport, :reported?, to: :feeder_item
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ module Feeder
2
+ module Concerns::Helpers
3
+ require 'feeder/concerns/helpers/filter'
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ module Feeder
2
+ module Concerns::Helpers::Filter
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ scope :filter, ->(options) {
7
+ args = []
8
+ wheres = options.each.map do |feedable, ids|
9
+ ids = feedable.pluck :id if ids == :all
10
+
11
+ args << feedable << ids
12
+
13
+ "(feedable_type = ? AND feedable_id IN (?))"
14
+ end.join " OR "
15
+
16
+ where(wheres, *(args))
17
+ }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module Feeder
2
+ module Concerns::Models
3
+ require 'feeder/concerns/models/item'
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ module Feeder
2
+ module Concerns::Models::Item
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Feeder::Concerns::Helpers::Filter
7
+
8
+ belongs_to :feedable, polymorphic: true
9
+
10
+ def type
11
+ feedable_type.underscore
12
+ end
13
+
14
+ def report
15
+ self.update reported: true
16
+ end
17
+
18
+ def block
19
+ self.update blocked: true
20
+ end
21
+
22
+ def unreport
23
+ self.update reported: false
24
+ end
25
+
26
+ def unblock
27
+ self.update blocked: false
28
+ end
29
+ end
30
+ end
31
+ end