diff --git a/Gemfile b/Gemfile index a7e2769..aa2c8c9 100644 --- a/Gemfile +++ b/Gemfile @@ -2,11 +2,6 @@ source 'https://rubygems.org' gem 'rails', '3.2.12' -# Bundle edge Rails instead: -# gem 'rails', :git => 'git://github.com/rails/rails.git' - - - # Gems used only for assets and not required # in production environments by default. group :assets do @@ -36,11 +31,16 @@ gem 'jquery-rails' # To use debugger # gem 'debugger' -gem 'mongo_mapper' -gem 'bson_ext' gem 'haml-rails' -gem 'mm-multi-parameter-attributes' gem 'feedzirra' gem 'htmlentities' gem 'sorcery' gem 'will_paginate', '~> 3.0' +gem 'activerecord-postgresql-adapter' +gem 'pg' + +group :development do + gem 'meta_request' + gem 'better_errors' + gem 'binding_of_caller' +end diff --git a/Gemfile.lock b/Gemfile.lock index 72ae3f3..269c60d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -22,6 +22,8 @@ GEM activesupport (= 3.2.12) arel (~> 3.0.2) tzinfo (~> 0.3.29) + activerecord-postgresql-adapter (0.0.1) + pg activeresource (3.2.12) activemodel (= 3.2.12) activesupport (= 3.2.12) @@ -30,10 +32,13 @@ GEM multi_json (~> 1.0) arel (3.0.2) bcrypt-ruby (3.0.1) - bson (1.8.2) - bson_ext (1.8.2) - bson (~> 1.8.2) + better_errors (0.6.0) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + binding_of_caller (0.7.1) + debug_inspector (>= 0.0.1) builder (3.0.4) + coderay (1.0.9) coffee-rails (3.2.2) coffee-script (>= 2.2.0) railties (~> 3.2.0) @@ -42,6 +47,7 @@ GEM execjs coffee-script-source (1.4.0) curb (0.7.18) + debug_inspector (0.0.2) erubis (2.7.0) execjs (1.4.0) multi_json (~> 1.0) @@ -81,16 +87,10 @@ GEM i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) + meta_request (0.2.1) + rack-contrib + rails mime-types (1.21) - mm-multi-parameter-attributes (0.2.2) - mongo_mapper (>= 0.9.0) - tzinfo - mongo (1.8.2) - bson (~> 1.8.2) - mongo_mapper (0.12.0) - activemodel (~> 3.0) - activesupport (~> 3.0) - plucky (~> 0.5.2) multi_json (1.6.1) multipart-post (1.1.5) nokogiri (1.4.7) @@ -101,12 +101,13 @@ GEM jwt (~> 0.1.4) multi_json (~> 1.0) rack (~> 1.2) - plucky (0.5.2) - mongo (~> 1.5) + pg (0.14.1) polyglot (0.3.3) rack (1.4.5) rack-cache (1.2) rack (>= 0.4) + rack-contrib (1.1.0) + rack (>= 0.9.1) rack-ssl (1.3.3) rack rack-test (0.6.2) @@ -160,14 +161,16 @@ PLATFORMS ruby DEPENDENCIES - bson_ext + activerecord-postgresql-adapter + better_errors + binding_of_caller coffee-rails (~> 3.2.1) feedzirra haml-rails htmlentities jquery-rails - mm-multi-parameter-attributes - mongo_mapper + meta_request + pg rails (= 3.2.12) sass-rails (~> 3.2.3) sorcery diff --git a/app/assets/javascripts/layers.js.coffee b/app/assets/javascripts/layers.js.coffee new file mode 100644 index 0000000..7615679 --- /dev/null +++ b/app/assets/javascripts/layers.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/assets/stylesheets/layers.css.scss b/app/assets/stylesheets/layers.css.scss new file mode 100644 index 0000000..3a0518b --- /dev/null +++ b/app/assets/stylesheets/layers.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Layers controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/feeds_controller.rb b/app/controllers/feeds_controller.rb index 77573f6..28cf6fc 100644 --- a/app/controllers/feeds_controller.rb +++ b/app/controllers/feeds_controller.rb @@ -17,8 +17,8 @@ class FeedsController < ApplicationController # GET /feeds/1.json def show @feed = Feed.find(params[:id]) - @posts = @feed.posts.sort(:published.desc).paginate(:page => params[:page], :per_page => 20) - + @posts = @feed.posts.order("published desc").paginate(:page => params[:page], :per_page => 20) + respond_to do |format| format.html # show.html.erb format.json { render json: @feed } @@ -44,11 +44,27 @@ class FeedsController < ApplicationController # POST /feeds # POST /feeds.json def create - @feed = Feed.new(params[:feed]) + + + 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 '/', notice: 'Feed added OK' } + format.html { redirect_to @layer, notice: 'Feed added OK' } format.json { render json: @feed, status: :created, location: @feed } else format.html { render action: "new" } @@ -87,12 +103,12 @@ class FeedsController < ApplicationController def fetch @feed = Feed.find(params[:id]) - @feed.get + @feed.fetch redirect_to :back, notice: 'Feed fetched OK' end def fetch_all - Feed.get_all + Feed.fetch_all redirect_to :back, notice: 'All feeds fetched OK' end end diff --git a/app/controllers/layers_controller.rb b/app/controllers/layers_controller.rb new file mode 100644 index 0000000..b69537d --- /dev/null +++ b/app/controllers/layers_controller.rb @@ -0,0 +1,85 @@ +class LayersController < ApplicationController + # GET /layers + # GET /layers.json + def index + @layers = Layer.all + + respond_to do |format| + format.html # index.html.erb + format.json { render json: @layers } + end + end + + # GET /layers/1 + # GET /layers/1.json + def show + @layer = Layer.find(params[:id]) + @feed = Feed.new + @feed.new_layer_id = @layer.id + + respond_to do |format| + format.html # show.html.erb + format.json { render json: @layer } + end + end + + # GET /layers/new + # GET /layers/new.json + def new + @layer = Layer.new + + respond_to do |format| + format.html # new.html.erb + format.json { render json: @layer } + end + end + + # GET /layers/1/edit + def edit + @layer = Layer.find(params[:id]) + end + + # POST /layers + # POST /layers.json + def create + @layer = Layer.new(params[:layer]) + + respond_to do |format| + if @layer.save + format.html { redirect_to @layer, notice: 'Layer was successfully created.' } + format.json { render json: @layer, status: :created, location: @layer } + else + format.html { render action: "new" } + format.json { render json: @layer.errors, status: :unprocessable_entity } + end + end + end + + # PUT /layers/1 + # PUT /layers/1.json + def update + @layer = Layer.find(params[:id]) + + respond_to do |format| + if @layer.update_attributes(params[:layer]) + format.html { redirect_to @layer, notice: 'Layer was successfully updated.' } + format.json { head :no_content } + else + format.html { render action: "edit" } + format.json { render json: @layer.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /layers/1 + # DELETE /layers/1.json + def destroy + @layer = Layer.find(params[:id]) + @layer.destroy + + respond_to do |format| + format.html { redirect_to layers_url } + format.json { head :no_content } + end + end +end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index c0c12fb..d53db30 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -2,13 +2,13 @@ class PostsController < ApplicationController include PostsHelper def near - @posts = Post.near(params[:lat].to_f, params[:lon].to_f, params[:radius].to_f) + @posts = Post.near(params[:lat].to_f, params[:lon].to_f, params[:radius].to_f, params[:layer_id]) - ErrorLog.create( - :ts => Time.now, - :params => params, - :pois_returned => @posts.size - ) +# ErrorLog.create( +# :ts => Time.now, +# :params => params, +# :pois_returned => @posts.size +# ) layar_response = { :layer => 'hyparlocal', diff --git a/app/helpers/layers_helper.rb b/app/helpers/layers_helper.rb new file mode 100644 index 0000000..b8dbc16 --- /dev/null +++ b/app/helpers/layers_helper.rb @@ -0,0 +1,2 @@ +module LayersHelper +end diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb index 8325f46..3e77a72 100644 --- a/app/helpers/posts_helper.rb +++ b/app/helpers/posts_helper.rb @@ -6,15 +6,15 @@ module PostsHelper :imageURL => "%s%s/assets/layar-icons/tal-logo-100.png" % [ request.protocol, request.env['HTTP_HOST'] ], :anchor => { :geolocation => { - :lat => post.loc['lat'], - :lon => post.loc['lng'], + :lat => post.lat, + :lon => post.lon, :alt => 0 } }, :text => { :title => decode_entities(post.title), :description => clean_description(post.summary), - :footnote => "From #{post.feed.title}" + :footnote => "From #{post.feed_title}" }, :actions => [ { diff --git a/app/models/feed.rb b/app/models/feed.rb index 2838749..e07fc45 100644 --- a/app/models/feed.rb +++ b/app/models/feed.rb @@ -1,57 +1,45 @@ -class Feed - include MongoMapper::Document +class Feed < ActiveRecord::Base + has_many :posts, :dependent => :destroy + has_and_belongs_to_many :layers + 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 - key :title, String, :default => "[New feed - hasn't been fetched yet]" - key :feed_url, String # The URL of the RSS feed, not the website that owns it - key :url, String # The URL of website. Called "link" in RSS 2.0 - key :description, String - key :guid, String # Atom id or RSS guid - key :generator, String - key :last_fetched, Time, :default => nil - timestamps! - - ensure_index :title - - many :posts, :dependent => :destroy - - validates :title, :presence => true validates_format_of :feed_url, :with => URI::regexp(%w(http https)), :message => "must be a valid URL" - after_create :get + after_create :fetch - # Fetch and parse feed contents from web - - def self.get_all - Feed.all.each { |f| f.get } + def self.fetch_all + Feed.all.each { |f| f.fetch } end - def get - puts "Fetching feed: #{@url}" + # Fetch and parse feed contents from web + def fetch + puts "Fetching feed: #{self.feed_url}" Feedzirra::Feed.add_common_feed_entry_element('georss:point', :as => :point) Feedzirra::Feed.add_common_feed_entry_element('geo:lat', :as => :geo_lat) Feedzirra::Feed.add_common_feed_entry_element('geo:long', :as => :geo_long) Feedzirra::Feed.add_common_feed_element('generator', :as => :generator) - feed = Feedzirra::Feed.fetch_and_parse(@feed_url) + feed = Feedzirra::Feed.fetch_and_parse(self.feed_url) - self.set( + self.update_attributes( :title => feed.title, :url => feed.url, :description => feed.description, :generator => feed.generator, - :last_fetched => Time.now + :last_fetched => DateTime.now ) feed.entries.each do |e| if e.geo_lat && e.geo_long - latlng = [e.geo_lat, e.geo_long] + latlon = [e.geo_lat, e.geo_long] elsif e.point - latlng = e.point.split(' ') + latlon = e.point.split(' ') else next - end - + end + attrs = { :title => e.title, :url => e.url, @@ -60,20 +48,15 @@ class Feed :content => e.content, :published => e.published, :guid => e.id, - :loc => { - :lng => latlng[1].to_f, - :lat => latlng[0].to_f - } + :lon => latlon[1].to_f, + :lat => latlon[0].to_f } - if Post.where(:url => e.url).size == 0 - self.posts << Post.create(attrs) - else - Post.set({:url => e.url}, attrs) - end - + # Create a new post or update an existing one + post = Post.find_or_initialize_by_url(e.url) + post.feed = self + post.assign_attributes(attrs) + post.save end - end - end diff --git a/app/models/layer.rb b/app/models/layer.rb new file mode 100644 index 0000000..86d2ff0 --- /dev/null +++ b/app/models/layer.rb @@ -0,0 +1,4 @@ +class Layer < ActiveRecord::Base + attr_accessible :name + has_and_belongs_to_many :feeds +end diff --git a/app/models/post.rb b/app/models/post.rb index 3841336..598edf4 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1,26 +1,70 @@ -class Post - include MongoMapper::Document - - key :title, String - key :url, String - key :author, String - key :summary, String - key :content, String - key :published, Time - key :loc, Hash # { lng, lat } - timestamps! - - ensure_index [[:loc, '2d']] - +class Post < ActiveRecord::Base belongs_to :feed - EARTH_RADIUS_M = 6378000 + EARTH_RADIUS_METRES = 6378000 - def self.near(lat, lng, radius_m) - all( - :loc => { - '$nearSphere' => [ lng, lat ], - '$maxDistance' => radius_m / EARTH_RADIUS_M - }) + def self.near(lat, lon, radius_metres, layer_id) + # Santize inputs. Is this necessary? + lat = lat.to_f + lon = lon.to_f + radius_metres = radius_metres.to_i + layer_id = layer_id.to_i + + # Calculate distance using the Haversine formula + self.find_by_sql(<<-ENDQUERY + SELECT + p.id, + p.title, + p.summary, + p.url, + p.lat, + p.lon, + p.published, + f.title as feed_title, + ( #{EARTH_RADIUS_METRES} + * acos( cos( radians('#{lat}') ) + * cos( radians( p.lat ) ) + * cos( radians( p.lon ) + - radians('#{lon}') ) + + sin( radians('#{lat}') ) + * sin( radians( p.lat ) ) ) ) + As distance + + FROM posts p + INNER JOIN feeds f + ON p.feed_id = f.id + + WHERE + p.id IN + ( -- Subquery returns a list of post_ids for posts on this layer + SELECT p.id + + FROM feeds_layers fl + + INNER JOIN feeds f + ON fl.feed_id = f.id + + INNER JOIN posts p + ON p.feed_id = f.id + + WHERE fl.layer_id = #{layer_id} + ) + + AND + ( + #{EARTH_RADIUS_METRES} + * acos( cos( radians('#{lat}') ) + * cos( radians( p.lat ) ) + * cos( radians( p.lon ) + - radians('#{lon}') ) + + sin( radians('#{lat}') ) + * sin( radians( p.lat ) ) ) + ) + <= #{radius_metres} + + ORDER BY distance + + ENDQUERY + ) end end diff --git a/app/models/user.rb b/app/models/user.rb index 4eeb5b4..1afe6a4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,11 +1,4 @@ -class User - include MongoMapper::Document - - key :email, String - key :crypted_password, String - key :salt, String - timestamps! - +class User < ActiveRecord::Base authenticates_with_sorcery! # attr_accessible :email, :password, :password_confirmation @@ -14,5 +7,4 @@ class User validates_uniqueness_of :email validates_length_of :password, :minimum => 3, :message => "password must be at least 3 characters long", :if => :password validates_confirmation_of :password, :message => "should match confirmation", :if => :password - end diff --git a/app/views/feeds/_form.html.haml b/app/views/feeds/_form.html.haml index 70a4b73..60e59dc 100644 --- a/app/views/feeds/_form.html.haml +++ b/app/views/feeds/_form.html.haml @@ -15,6 +15,7 @@ .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 diff --git a/app/views/feeds/show.html.haml b/app/views/feeds/show.html.haml index fe65166..4863bc5 100644 --- a/app/views/feeds/show.html.haml +++ b/app/views/feeds/show.html.haml @@ -28,7 +28,7 @@ - @posts.each do |p| %tr %td= link_to p.title, p.url - %td= link_to "Map", "https://maps.google.co.uk/maps?q=%s,%s&hl=en&z=18" % [ p.loc['lat'], p.loc['lng'] ], :target => "_blank" + %td= link_to "Map", "https://maps.google.co.uk/maps?q=%s,%s&hl=en&z=18" % [ p.lat, p.lon ], :target => "_blank" %td - unless p.published.nil? = p.published.strftime("%d %b %Y %H:%M") diff --git a/app/views/layers/_form.html.haml b/app/views/layers/_form.html.haml new file mode 100644 index 0000000..27bc898 --- /dev/null +++ b/app/views/layers/_form.html.haml @@ -0,0 +1,13 @@ += form_for @layer do |f| + - if @layer.errors.any? + #error_explanation + %h2= "#{pluralize(@layer.errors.count, "error")} prohibited this layer from being saved:" + %ul + - @layer.errors.full_messages.each do |msg| + %li= msg + + .field + = f.label :name + = f.text_field :name + .actions + = f.submit 'Save' diff --git a/app/views/layers/edit.html.haml b/app/views/layers/edit.html.haml new file mode 100644 index 0000000..9c013b7 --- /dev/null +++ b/app/views/layers/edit.html.haml @@ -0,0 +1,7 @@ +%h1 Editing layer + += render 'form' + += link_to 'Show', @layer +\| += link_to 'Back', layers_path diff --git a/app/views/layers/index.html.haml b/app/views/layers/index.html.haml new file mode 100644 index 0000000..1b773c5 --- /dev/null +++ b/app/views/layers/index.html.haml @@ -0,0 +1,10 @@ +%h1 Layers + += link_to 'New Layer', new_layer_path, :class => 'button' + +%table + - @layers.each do |layer| + %tr + %td= link_to layer.name, layer + %td= link_to 'Edit', edit_layer_path(layer) + %td= link_to 'Delete', layer, :method => :delete, :data => { :confirm => 'Are you sure?' } diff --git a/app/views/layers/new.html.haml b/app/views/layers/new.html.haml new file mode 100644 index 0000000..73ddc96 --- /dev/null +++ b/app/views/layers/new.html.haml @@ -0,0 +1,5 @@ +%h1 New layer + += render 'form' + += link_to 'Back', layers_path diff --git a/app/views/layers/show.html.haml b/app/views/layers/show.html.haml new file mode 100644 index 0000000..546aaeb --- /dev/null +++ b/app/views/layers/show.html.haml @@ -0,0 +1,51 @@ +%h1 + Layer: + = @layer.name + +%p + API URL: + = "#{request.protocol}#{request.host_with_port}/posts/near.json?layer_id=#{@layer.id}" + + +%h2 Feeds + +#new_feed + = render 'feeds/form' + +%p= link_to "Fetch all", :fetch_all, :class => "button" + +%table + %tr + %th + %th Posts + %th Fetched + %th + %th + %th + + - @layer.feeds.each do |f| + %tr + %td + .feed_title + = link_to f.title, f + %td.right= f.posts.count + %td + - if f.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" + + %tr + %td + %td.right [number of posts in this layer] + %td + %td + %td + %td + += link_to 'Edit', edit_layer_path(@layer) +\| += link_to 'Back', layers_path diff --git a/app/views/posts/near.html.haml b/app/views/posts/near.html.haml index 3a14668..f967592 100644 --- a/app/views/posts/near.html.haml +++ b/app/views/posts/near.html.haml @@ -1,11 +1,20 @@ %h1 Posts#near %table + %thead + %tr + %th + %th Latitude + %th Longitude + %th Distance (metres) + %th Published + %tbody - @posts.each do |p| %tr %td= link_to p.title, p.url - %td= p.loc['lat'] - %td= p.loc['lng'] + %td= p.lat + %td= p.lon + %td= p.distance.to_i %td - unless p.published.nil? = p.published.strftime("%d %b %Y %H:%M") diff --git a/config/application.rb b/config/application.rb index 426326e..57fa486 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,12 +1,13 @@ require File.expand_path('../boot', __FILE__) # Pick the frameworks you want: +require 'rails/all' # require "active_record/railtie" -require "action_controller/railtie" -require "action_mailer/railtie" -require "active_resource/railtie" -require "sprockets/railtie" -require "rails/test_unit/railtie" +# require "action_controller/railtie" +# require "action_mailer/railtie" +# require "active_resource/railtie" +# require "sprockets/railtie" +# require "rails/test_unit/railtie" if defined?(Bundler) # If you precompile assets before deploying to production, use this line @@ -15,7 +16,6 @@ if defined?(Bundler) # Bundler.require(:default, :assets, Rails.env) end - module Apollo class Application < Rails::Application config.action_mailer.default_url_options = { :host => ENV['APOLLO_HOSTNAME'] } @@ -72,10 +72,7 @@ module Apollo # http://mongomapper.com/documentation/getting-started/rails.html config.generators do |g| g.template_engine :haml - g.orm :mongo_mapper end - - MongoMapper::Document.plugin(MongoMapper::Plugins::MultiParameterAttributes) # Time.zone = 'London' end end diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..4904f45 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,26 @@ +development: + adapter: postgresql + encoding: unicode + database: apollo_development + pool: 5 + username: postgres + password: + port: 5433 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: postgresql + encoding: unicode + database: apollo_test + pool: 5 + username: postgres + password: + port: 5433 + +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 diff --git a/config/mongo.yml b/config/mongo.yml deleted file mode 100644 index 5f11e48..0000000 --- a/config/mongo.yml +++ /dev/null @@ -1,15 +0,0 @@ -defaults: &defaults - host: 127.0.0.1 - port: 27017 - -development: - <<: *defaults - database: apollo_development - -test: - <<: *defaults - database: apollo_test - -# set these environment variables on your prod server -production: - uri: <%= ENV['MONGOLAB_URI'] %> diff --git a/config/routes.rb b/config/routes.rb index 6cbea69..81b3caa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,7 @@ Apollo::Application.routes.draw do get "logout" => "sessions#destroy", :as => "logout" get "login" => "sessions#new", :as => "login" + resources :layers resources :users resources :sessions resources :password_resets @@ -65,7 +66,7 @@ Apollo::Application.routes.draw do # You can have the root of your site routed with "root" # just remember to delete public/index.html. - root :to => 'feeds#index' + root :to => 'layers#index' # See how all your routes lay out with "rake routes" diff --git a/db/migrate/20130222112030_sorcery_core.rb b/db/migrate/20130222112030_sorcery_core.rb new file mode 100644 index 0000000..24956fe --- /dev/null +++ b/db/migrate/20130222112030_sorcery_core.rb @@ -0,0 +1,16 @@ +class SorceryCore < ActiveRecord::Migration + def self.up + create_table :users do |t| + t.string :username, :null => false # if you use another field as a username, for example email, you can safely remove this field. + t.string :email, :default => nil # if you use this field as a username, you might want to make it :null => false. + t.string :crypted_password, :default => nil + t.string :salt, :default => nil + + t.timestamps + end + end + + def self.down + drop_table :users + end +end \ No newline at end of file diff --git a/db/migrate/20130222120002_remove_user_username.rb b/db/migrate/20130222120002_remove_user_username.rb new file mode 100644 index 0000000..4bf89bd --- /dev/null +++ b/db/migrate/20130222120002_remove_user_username.rb @@ -0,0 +1,9 @@ +class RemoveUserUsername < ActiveRecord::Migration + def up + remove_column :users, :username + end + + def down + add_column :users, :username, :string, :null => false + end +end diff --git a/db/migrate/20130222123224_add_guid_to_posts_remove_guid_from_feeds.rb b/db/migrate/20130222123224_add_guid_to_posts_remove_guid_from_feeds.rb new file mode 100644 index 0000000..d2a4d35 --- /dev/null +++ b/db/migrate/20130222123224_add_guid_to_posts_remove_guid_from_feeds.rb @@ -0,0 +1,6 @@ +class AddGuidToPostsRemoveGuidFromFeeds < ActiveRecord::Migration + def change + remove_column :feeds, :guid + add_column :posts, :guid, :string + end +end diff --git a/db/migrate/20130222123833_feeds_change_last_fetched_to_datetime.rb b/db/migrate/20130222123833_feeds_change_last_fetched_to_datetime.rb new file mode 100644 index 0000000..22aabf0 --- /dev/null +++ b/db/migrate/20130222123833_feeds_change_last_fetched_to_datetime.rb @@ -0,0 +1,11 @@ +class FeedsChangeLastFetchedToDatetime < ActiveRecord::Migration + def up + remove_column :feeds, :last_fetched + add_column :feeds, :last_fetched, :datetime + end + + def down + remove_column :feeds, :last_fetched + add_column :feeds, :last_fetched, :time + end +end diff --git a/db/migrate/20130222125833_post_published_change_to_datetime.rb b/db/migrate/20130222125833_post_published_change_to_datetime.rb new file mode 100644 index 0000000..3e27fbf --- /dev/null +++ b/db/migrate/20130222125833_post_published_change_to_datetime.rb @@ -0,0 +1,11 @@ +class PostPublishedChangeToDatetime < ActiveRecord::Migration + def up + remove_column :posts, :published + add_column :posts, :published, :datetime + end + + def down + remove_column :posts, :published + add_column :posts, :published, :time + end +end diff --git a/db/migrate/20130228150425_create_layers.rb b/db/migrate/20130228150425_create_layers.rb new file mode 100644 index 0000000..4ad96d7 --- /dev/null +++ b/db/migrate/20130228150425_create_layers.rb @@ -0,0 +1,15 @@ +class CreateLayers < ActiveRecord::Migration + def change + create_table :layers do |t| + t.string :name + t.timestamps + end + + create_table :feeds_layers, :id => false do |t| + t.integer :feed_id + t.integer :layer_id + end + + add_index :feeds_layers, [ :feed_id, :layer_id ] + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..f6c1318 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,63 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended to check this file into your version control system. + +ActiveRecord::Schema.define(:version => 20130228150425) do + + create_table "feeds", :force => true do |t| + t.string "title" + t.string "feed_url" + t.string "url" + t.string "description" + t.string "generator" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + 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 + t.datetime "updated_at", :null => false + end + + create_table "posts", :force => true do |t| + t.string "title" + t.string "url" + t.string "author" + t.text "summary" + t.text "content" + t.decimal "lat" + t.decimal "lon" + t.integer "feed_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "guid" + t.datetime "published" + end + + 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 + end + +end diff --git a/test/fixtures/layers.yml b/test/fixtures/layers.yml new file mode 100644 index 0000000..0227c60 --- /dev/null +++ b/test/fixtures/layers.yml @@ -0,0 +1,7 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html + +one: + name: MyString + +two: + name: MyString diff --git a/test/functional/layers_controller_test.rb b/test/functional/layers_controller_test.rb new file mode 100644 index 0000000..d6dc765 --- /dev/null +++ b/test/functional/layers_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class LayersControllerTest < ActionController::TestCase + setup do + @layer = layers(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:layers) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create layer" do + assert_difference('Layer.count') do + post :create, layer: { name: @layer.name } + end + + assert_redirected_to layer_path(assigns(:layer)) + end + + test "should show layer" do + get :show, id: @layer + assert_response :success + end + + test "should get edit" do + get :edit, id: @layer + assert_response :success + end + + test "should update layer" do + put :update, id: @layer, layer: { name: @layer.name } + assert_redirected_to layer_path(assigns(:layer)) + end + + test "should destroy layer" do + assert_difference('Layer.count', -1) do + delete :destroy, id: @layer + end + + assert_redirected_to layers_path + end +end diff --git a/test/unit/helpers/layers_helper_test.rb b/test/unit/helpers/layers_helper_test.rb new file mode 100644 index 0000000..ad81667 --- /dev/null +++ b/test/unit/helpers/layers_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class LayersHelperTest < ActionView::TestCase +end diff --git a/test/unit/layer_test.rb b/test/unit/layer_test.rb new file mode 100644 index 0000000..29c3aa3 --- /dev/null +++ b/test/unit/layer_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class LayerTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end