diff --git a/app/assets/javascripts/subscriptions.js.coffee b/app/assets/javascripts/subscriptions.js.coffee new file mode 100644 index 0000000..7615679 --- /dev/null +++ b/app/assets/javascripts/subscriptions.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/controllers/feeds_controller.rb b/app/controllers/feeds_controller.rb index 28cf6fc..d1836fd 100644 --- a/app/controllers/feeds_controller.rb +++ b/app/controllers/feeds_controller.rb @@ -41,37 +41,7 @@ class FeedsController < ApplicationController @feed = Feed.find(params[:id]) end - # POST /feeds - # POST /feeds.json - def create - - - Rails.logger.debug "Feed URL: %s" % params['feed']['feed_url'] - - if Feed.where(:feed_url => params['feed']['feed_url']).size == 1 # ensure this test returns the values we expect - Rails.logger.debug "Adding existing feed to a new layer" - @feed = Feed.where(:feed_url => params['feed']['feed_url']).first - @layer = Layer.find(params['feed']['new_layer_id']) # assumes that the specified layer exists - # Attach the existing feed to the specified layer (making sure we only add each one once) - @feed.layers << @layer unless @feed.layers.include?(@layer) - else - # Create a new feed - Rails.logger.debug "Creating a new feed" - @feed = Feed.new(params[:feed]) - @layer = Layer.find(params['feed']['new_layer_id']) # assumes that the specified layer exists - @feed.layers << @layer unless @feed.layers.include?(@layer) - end - - respond_to do |format| - if @feed.save - format.html { redirect_to @layer, notice: 'Feed added OK' } - format.json { render json: @feed, status: :created, location: @feed } - else - format.html { render action: "new" } - format.json { render json: @feed.errors, status: :unprocessable_entity } - end - end - end + # Feeds are created through the Subscriptions controller # PUT /feeds/1 # PUT /feeds/1.json diff --git a/app/controllers/layers_controller.rb b/app/controllers/layers_controller.rb index 167b0ae..b8532ed 100644 --- a/app/controllers/layers_controller.rb +++ b/app/controllers/layers_controller.rb @@ -16,8 +16,7 @@ class LayersController < ApplicationController # GET /layers/1.json def show @layer = Layer.find(params[:id]) - @feed = Feed.new - @feed.new_layer_id = @layer.id + @subscription = Subscription.new(:layer_id => @layer.id) respond_to do |format| format.html # show.html.erb diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb new file mode 100644 index 0000000..1f1e377 --- /dev/null +++ b/app/controllers/subscriptions_controller.rb @@ -0,0 +1,46 @@ +class SubscriptionsController < ApplicationController + def create + Rails.logger.debug "Feed URL: %s" % params['subscription']['feed_url'] + + if Feed.where(:feed_url => params['subscription']['feed_url']).size == 1 # ensure this test returns the values we expect + Rails.logger.debug "Adding existing feed to a new layer" + @feed = Feed.where(:feed_url => params['subscription']['feed_url']).first + @layer = Layer.find(params['subscription']['layer_id']) # assumes that the specified layer exists + # Attach the existing feed to the specified layer (making sure we only add each one once) + # @feed.layers << @layer unless @feed.layers.include?(@layer) + else + # Create a new feed + Rails.logger.debug "Creating a new feed" + @feed = Feed.create(:feed_url => params[:subscription][:feed_url]) + @layer = Layer.find(params['subscription']['layer_id']) # assumes that the specified layer exists + # @feed.layers << @layer unless @feed.layers.include?(@layer) + end + + @subscription = Subscription.new(:feed => @feed, :layer => @layer) + + begin + respond_to do |format| + if @subscription.save + format.html { redirect_to @layer, notice: 'Feed added OK' } + format.json { render json: @feed, status: :created, location: @feed } + else + format.html { render action: "new" } + format.json { render json: @feed.errors, status: :unprocessable_entity } + end + end + rescue ActiveRecord::RecordNotUnique + redirect_to @layer, alert: 'That feed is already on this layer' + end + end + + def destroy + @subscription = Subscription.find(params[:id]) + @layer = @subscription.layer + @subscription.destroy + + respond_to do |format| + format.html { redirect_to @layer, notice: 'Subscription deleted OK' } + format.json { head :no_content } + end + end +end diff --git a/app/helpers/subscriptions_helper.rb b/app/helpers/subscriptions_helper.rb new file mode 100644 index 0000000..9ace045 --- /dev/null +++ b/app/helpers/subscriptions_helper.rb @@ -0,0 +1,2 @@ +module SubscriptionsHelper +end diff --git a/app/models/feed.rb b/app/models/feed.rb index e07fc45..516b47d 100644 --- a/app/models/feed.rb +++ b/app/models/feed.rb @@ -1,6 +1,7 @@ class Feed < ActiveRecord::Base has_many :posts, :dependent => :destroy - has_and_belongs_to_many :layers + has_many :subscriptions + has_many :layers, :through => :subscriptions, :uniq => true attr_accessible :title, :url, :description, :generator, :last_fetched, :feed_url attr_accessor :new_layer_id # non model attribute used when creating new feeds from within a layer diff --git a/app/models/layer.rb b/app/models/layer.rb index 86d2ff0..73e64aa 100644 --- a/app/models/layer.rb +++ b/app/models/layer.rb @@ -1,4 +1,5 @@ class Layer < ActiveRecord::Base attr_accessible :name - has_and_belongs_to_many :feeds + has_many :subscriptions + has_many :feeds, :through => :subscriptions, :uniq => true end diff --git a/app/models/subscription.rb b/app/models/subscription.rb new file mode 100644 index 0000000..9b12662 --- /dev/null +++ b/app/models/subscription.rb @@ -0,0 +1,5 @@ +class Subscription < ActiveRecord::Base + attr_accessor :feed_url + belongs_to :feed + belongs_to :layer +end diff --git a/app/views/feeds/_form.html.haml b/app/views/feeds/_form.html.haml index 60e59dc..9205769 100644 --- a/app/views/feeds/_form.html.haml +++ b/app/views/feeds/_form.html.haml @@ -8,17 +8,8 @@ - @feed.errors.full_messages.each do |msg| %li= msg - .field - -# - = f.label :title - = f.text_field :title .field = f.label "URL" = f.text_field :feed_url, :size => 120 - = f.hidden_field :new_layer_id, :value => @feed.new_layer_id = f.submit 'Save', :id => 'submit' - -# - .field - = f.label :last_fetched - = f.datetime_select :last_fetched .actions diff --git a/app/views/layers/show.html.haml b/app/views/layers/show.html.haml index 546aaeb..66bf172 100644 --- a/app/views/layers/show.html.haml +++ b/app/views/layers/show.html.haml @@ -10,7 +10,7 @@ %h2 Feeds #new_feed - = render 'feeds/form' + = render 'subscriptions/form' %p= link_to "Fetch all", :fetch_all, :class => "button" @@ -23,20 +23,20 @@ %th %th - - @layer.feeds.each do |f| + - @layer.subscriptions.each do |s| %tr %td .feed_title - = link_to f.title, f - %td.right= f.posts.count + = link_to s.feed.title, s.feed + %td.right= s.feed.posts.count %td - - if f.last_fetched.nil? + - if s.feed.last_fetched.nil? never - else - = (time_ago_in_words(f.last_fetched) + " ago").gsub(/ +/, " ").html_safe - %td= link_to "Fetch", fetch_feed_url(f), :class => "button" - %td= link_to 'Edit', edit_feed_path(f), :class => "button" - %td= link_to 'Delete', f, :confirm => "Delete #{f.title} and all its posts?", :method => :delete, :class => "button" + = (time_ago_in_words(s.feed.last_fetched) + " ago").gsub(/ +/, " ").html_safe + %td= link_to "Fetch", fetch_feed_url(s.feed), :class => "button" + %td= link_to 'Edit', edit_feed_path(s.feed), :class => "button" + %td= link_to 'Delete', s, :confirm => "Delete #{s.feed.title}?", :method => :delete, :class => "button" %tr %td diff --git a/app/views/subscriptions/_form.html.haml b/app/views/subscriptions/_form.html.haml new file mode 100644 index 0000000..2502b5a --- /dev/null +++ b/app/views/subscriptions/_form.html.haml @@ -0,0 +1,16 @@ += form_for @subscription do |f| + %h2 Add a feed + + -if @subscription.errors.any? + #error_explanation + %h2= "#{pluralize(@subscription.errors.count, "error")} prohibited this feed from being saved:" + %ul + - @subscription.errors.full_messages.each do |msg| + %li= msg + + .field + = f.label "URL" + = f.text_field :feed_url, :size => 120 + = f.hidden_field :layer_id, :value => @subscription.layer.id + = f.submit 'Save', :id => 'submit' + .actions diff --git a/config/application.rb b/config/application.rb index 52bc9f2..f12a1d9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -75,6 +75,7 @@ module Apollo # http://mongomapper.com/documentation/getting-started/rails.html config.generators do |g| g.template_engine :haml + g.stylesheets false end # Time.zone = 'London' end diff --git a/config/routes.rb b/config/routes.rb index 81b3caa..8cf85ae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,10 +6,11 @@ Apollo::Application.routes.draw do resources :users resources :sessions resources :password_resets + resources :subscriptions get "posts/near" - resources :feeds do + resources :feeds, :except => :create do member do get 'fetch' end diff --git a/db/migrate/20130321173521_convert_feeds_layers_table_to_subscriptions.rb b/db/migrate/20130321173521_convert_feeds_layers_table_to_subscriptions.rb new file mode 100644 index 0000000..7187ef0 --- /dev/null +++ b/db/migrate/20130321173521_convert_feeds_layers_table_to_subscriptions.rb @@ -0,0 +1,7 @@ +class ConvertFeedsLayersTableToSubscriptions < ActiveRecord::Migration + def change + rename_table :feeds_layers, :subscriptions + add_column :subscriptions, :id, :primary_key + add_index :subscriptions, :id, :name => "id" + end +end diff --git a/db/migrate/20130408142010_add_timestamps_to_subscriptions.rb b/db/migrate/20130408142010_add_timestamps_to_subscriptions.rb new file mode 100644 index 0000000..4382b72 --- /dev/null +++ b/db/migrate/20130408142010_add_timestamps_to_subscriptions.rb @@ -0,0 +1,5 @@ +class AddTimestampsToSubscriptions < ActiveRecord::Migration + def change + add_timestamps :subscriptions + end +end diff --git a/db/schema.rb b/db/schema.rb index 37951d4..859b218 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,23 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130320181527) do +ActiveRecord::Schema.define(:version => 20130408142010) do + + create_table "delayed_jobs", :force => true do |t| + t.integer "priority", :default => 0 + t.integer "attempts", :default => 0 + t.text "handler" + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" create_table "feeds", :force => true do |t| t.string "title" @@ -24,13 +40,6 @@ ActiveRecord::Schema.define(:version => 20130320181527) do t.datetime "last_fetched" end - create_table "feeds_layers", :id => false, :force => true do |t| - t.integer "feed_id" - t.integer "layer_id" - end - - add_index "feeds_layers", ["feed_id", "layer_id"], :name => "index_feeds_layers_on_feed_id_and_layer_id" - create_table "layers", :force => true do |t| t.string "name" t.datetime "created_at", :null => false @@ -52,14 +61,27 @@ ActiveRecord::Schema.define(:version => 20130320181527) do t.datetime "published" end + create_table "subscriptions", :force => true do |t| + t.integer "feed_id" + t.integer "layer_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "subscriptions", ["feed_id", "layer_id"], :name => "index_feeds_layers_on_feed_id_and_layer_id", :unique => true + add_index "subscriptions", ["id"], :name => "id" + create_table "users", :force => true do |t| t.string "email" t.string "crypted_password" t.string "salt" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "remember_me_token" t.datetime "remember_me_token_expires_at" + t.string "reset_password_token" + t.datetime "reset_password_token_expires_at" + t.datetime "reset_password_email_sent_at" end add_index "users", ["remember_me_token"], :name => "index_users_on_remember_me_token" diff --git a/test/fixtures/subscriptions.yml b/test/fixtures/subscriptions.yml new file mode 100644 index 0000000..c63aac0 --- /dev/null +++ b/test/fixtures/subscriptions.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/functional/subscriptions_controller_test.rb b/test/functional/subscriptions_controller_test.rb new file mode 100644 index 0000000..e129007 --- /dev/null +++ b/test/functional/subscriptions_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SubscriptionsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/helpers/subscriptions_helper_test.rb b/test/unit/helpers/subscriptions_helper_test.rb new file mode 100644 index 0000000..e6d7a6e --- /dev/null +++ b/test/unit/helpers/subscriptions_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class SubscriptionsHelperTest < ActionView::TestCase +end diff --git a/test/unit/subscription_test.rb b/test/unit/subscription_test.rb new file mode 100644 index 0000000..a045d1e --- /dev/null +++ b/test/unit/subscription_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SubscriptionTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end