title: Posts Model & Controller Architecture
body: <p>I am trying to develop a simple site that lets users add posts and view them all in one aggregated stream.</p>
  
  <p>The problem is that I have posts of 2 "types", the "message" post and the "link" post.</p>
  
  <p>All post must have a message and post can have a link. </p>
  
  <p>If post has a link it must be unique so you cant add a post with link that already was submitted (by you or some other user).</p>
  
  <p>So, in case if user adds the Post with provided link URL I need validations for link like:</p>
  
  <ul>
  <li>is this a link?</li>
  <li>is this link new (not already in db)?</li>
  <li>is this a valid link (like the domain exists and the server response is adequate (400,301,...)</li>
  </ul>
  
  <p>Now I am stuck with just one model for all the posts (with links and without them) that looks like this:</p>
  
  <pre><code>#
  # Table name: posts
  #
  #  id           :integer(4)      not null, primary key
  #  message      :string(255)     default(""), not null
  #  link         :string(2000)
  #  src_site_id  :integer(4)
  #  link_type    :integer(4)      default(0)
  #  explicit     :boolean(1)      default(FALSE)
  #  view_times   :integer(4)
  #  hidden_times :integer(4)
  #  tasted_times :integer(4)
  #  uid          :integer(4)
  #  created_at   :datetime
  #  updated_at   :datetime
  #
  
  class Post &lt; ActiveRecord::Base
    default_scope :order =&gt; 'created_at desc'
  
    attr_accessible :link, :message, :explicit
  
    validates :message, :presence =&gt; true,
                        :length   =&gt; { :maximum =&gt; 255 }
  
  end
  </code></pre>
  
  <p>The problem as I see it is that I cant apply model validation to link (cant check uniqueness or format) because it can be NULL, so I apply all the validations in posts_controller like this:</p>
  
  <pre><code>class PostsController &lt; ApplicationController
  
    def create
      @post = Post.new(params[:post])
  
      if @post.link.empty?
         @post.link = nil
         @post.save
      else 
        if looks_like_link(@post.link) 
          if is_new_link(@post.link) 
            if is_valid_link (@post.link)
              @post.save
            else # url is not available
              flash['error'] = 'link is not available'
            end
          else # link is already in db
              flash['error'] = 'link is already added'
          end
        else 
          flash['error'] = 'doesnt look like a link'
        end
  
      end
  
      redirect_to(root_path)
    end
  
    private
  
    def looks_like_link(link)
      link.match(/^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/ix)? true : false
    end 
  
    def is_new_link(link)
      Post.find_by_link(link) ? false : true
    end
  
    def is_valid_link(link)
      require "net/http"
      url = URI.parse(link)
      req = Net::HTTP.new(url.host, url.port)
      res = req.request_head(url.path)
  
      flash[:error] = 'res code is ' + res.code 
      return res.code == '200' || res.code == '301' ? true : false
  
    end
  end
  </code></pre>
  
  <p>How to make this the right way? I got a suggestion to use STI for this, but I don't really know how to do it the right way and how to apply validation. If you know of good resource about using STI and validations please give me a link.</p>