Author: assaf
Date: Thu Jun  5 16:26:42 2008
New Revision: 663778

URL: http://svn.apache.org/viewvc?rev=663778&view=rev
Log:
Specced Task::Rendering, changed form_completing field to integrated_ui, 
use_description? no longer used.
Passing task_url to perform/details frame only when integrated_ui is true.
Task over_due? now returns true or false.

Modified:
    ode/sandbox/singleshot/app/helpers/task_helper.rb
    ode/sandbox/singleshot/app/models/task.rb
    ode/sandbox/singleshot/app/views/tasks/show.html.erb
    ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb
    ode/sandbox/singleshot/db/schema.rb
    ode/sandbox/singleshot/lib/tasks/populate.rake
    ode/sandbox/singleshot/public/stylesheets/default.css
    ode/sandbox/singleshot/spec/models/task_spec.rb

Modified: ode/sandbox/singleshot/app/helpers/task_helper.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/helpers/task_helper.rb?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/helpers/task_helper.rb (original)
+++ ode/sandbox/singleshot/app/helpers/task_helper.rb Thu Jun  5 16:26:42 2008
@@ -26,20 +26,6 @@
     end
   end
 
-  def task_frame(task, performing)
-    state_uri = URI(task_perform_url(task))
-    state_uri.user, state_uri.password = '_token', 
task.token_for(authenticated)
-    params = { 'task_url'=>state_uri.to_s }
-    if performing
-      uri = URI(task.rendering.perform_url)
-      params.update 'complete_url'=>complete_redirect_tasks_url if 
task.rendering.completing
-    else
-      uri = URI(task.rendering.details_url)
-    end
-    uri.query = CGI.parse(uri.query || '').update(params).to_query
-    content_tag 'iframe', '', :id=>'task_frame', :src=>uri.to_s
-  end
-
   def task_actions(task)
     actions = []
     actions << button_to('Cancel', task_url(task, 
'task[status]'=>'cancelled'), :method=>:put, :title=>'Cancel this task') if 
task.can_cancel?(authenticated)

Modified: ode/sandbox/singleshot/app/models/task.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/models/task.rb?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/models/task.rb (original)
+++ ode/sandbox/singleshot/app/models/task.rb Thu Jun  5 16:26:42 2008
@@ -3,22 +3,22 @@
 #
 # Table name: tasks
 #
-#  id              :integer         not null, primary key
-#  title           :string(255)     not null
-#  description     :string(255)     not null
-#  priority        :integer(1)      not null
-#  due_on          :date
-#  status          :string(255)     not null
-#  perform_url     :string(255)
-#  details_url     :string(255)
-#  form_completing :boolean
-#  outcome_url     :string(255)
-#  outcome_type    :string(255)
-#  access_key      :string(32)
-#  data            :text            not null
-#  version         :integer         default(0), not null
-#  created_at      :datetime
-#  updated_at      :datetime
+#  id            :integer         not null, primary key
+#  title         :string(255)     not null
+#  description   :string(255)     not null
+#  priority      :integer(1)      not null
+#  due_on        :date
+#  status        :string(255)     not null
+#  perform_url   :string(255)
+#  details_url   :string(255)
+#  integrated_ui :boolean
+#  outcome_url   :string(255)
+#  outcome_type  :string(255)
+#  access_key    :string(32)
+#  data          :text            not null
+#  version       :integer         default(0), not null
+#  created_at    :datetime
+#  updated_at    :datetime
 #
 
 require 'openssl'
@@ -82,29 +82,27 @@
   validate do |task|
     # Check state transitions.
     from, to = task.status_change
-    if case from # States you cannot transition from.
-      when 'suspended'
-        task.errors.add :status, 'You are not allowed to resume this task.' 
unless task.admin?(task.modified_by)
-      when 'completed'
-        task.errors.add :status, 'Cannot change status of completed task.'
-      when 'cancelled'
-        task.errors.add :status, 'Cannot change status of cancelled task.'
-      end
-    else
-      case to # or, states you cannot transition to.
-      when 'reserved'
-        task.errors.add :status, 'Cannot change status to reserved.' unless 
from.nil?
-      when 'active'
-        #task.errors.add :status, "#{task.owner.fullname} is not allowed to 
claim this task." unless
-        #  task.potential_owners.empty? || task.potential_owner?(task.owner) 
|| task.admin?(task.owner)
-      when 'suspended'
-        task.errors.add :status, 'You are not allowed to suspend this task.' 
unless task.admin?(task.modified_by)
-      when 'completed'
-        task.errors.add :status, 'Cannot change to completed from any status 
but active.' unless from =='active'
-        task.errors.add :status, 'Only owner can complete task.' unless 
task.owner && task.modified_by == task.owner && !task.owner_changed?
-      when 'cancelled'
-        task.errors.add :status, 'You are not allowed to cancel this task.' 
unless task.admin?(task.modified_by)
-      end
+    case from # States you cannot transition from.
+    when 'suspended'
+      task.errors.add :status, 'You are not allowed to resume this task.' 
unless task.admin?(task.modified_by)
+    when 'completed'
+      task.errors.add :status, 'Cannot change status of completed task.'
+    when 'cancelled'
+      task.errors.add :status, 'Cannot change status of cancelled task.'
+    end
+    case to # or, states you cannot transition to.
+    when 'reserved'
+      task.errors.add :status, 'Cannot change status to reserved.' unless 
from.nil?
+    when 'active'
+      #task.errors.add :status, "#{task.owner.fullname} is not allowed to 
claim this task." unless
+      #  task.potential_owners.empty? || task.potential_owner?(task.owner) || 
task.admin?(task.owner)
+    when 'suspended'
+      task.errors.add :status, 'You are not allowed to suspend this task.' 
unless task.admin?(task.modified_by)
+    when 'completed'
+      task.errors.add :status, 'Cannot change to completed from any status but 
active.' unless from =='active'
+      task.errors.add :status, 'Only owner can complete task.' unless 
task.owner && task.modified_by == task.owner && !task.owner_changed?
+    when 'cancelled'
+      task.errors.add :status, 'You are not allowed to cancel this task.' 
unless task.admin?(task.modified_by)
     end
     task.readonly! if !task.status_changed? && (task.completed? || 
task.cancelled?)
   end
@@ -113,47 +111,63 @@
   # -- View and perform ---
 
   # Some tasks are performed offline, for example, calling a customer.  Other
-  # tasks are performed online, in which case we would like to include the UI
-  # for performing the task as part of the task page.
+  # tasks performed onlined, in which case we would like to render that UI
+  # component as part of the task view.
+  #
+  # There are two possible views for each task.  One view, presented to the
+  # task owner for performing the task, the other view presented to everyone
+  # else and only provides details about the task.
   #
-  # There are two views for each task.  One view presented to the task owner
-  # for performing the task, the other view, presented to everyone else only
-  # provides details about the task.
+  # Some UIs are integrated with the task manager: they obtain the task state
+  # and update it upon completion.  Other UIs require that the user mark the
+  # task upon completion.
   #
-  # Some forms are integrated with the task manager, these know how to update
-  # the task status and mark the task as completed.  For all other forms, we
-  # need to include a button to mark the task as completed.
+  # Tasks that do not have a UI representation (e.g. offline tasks) should use
+  # the task description as the most adequate representation.  Calling
+  # #render_url on these tasks returns nil.  Tasks that do have a UI
+  # representation should use the URL returned by #render_url, e.g. to pull
+  # that UI into an IFrame.
   #
-  # We handle these cases through several combinations of rendering
-  # information.  Some tasks are rendered using only the task description (e.g.
-  # offline tasks).  Other tasks provide a URL for performing the task, using
-  # the description for everyone else.  Last, some tasks provide both a URL for
-  # performing the task and a URL for viewing task details.
+  # UIs that integrate with the taske manager (#integrated_ui) will need
+  # additional query parameters in the URL, those are passed to render_for
+  # using an argument/block.  UIs that are not integrated should provide the
+  # user with other means for marking the task as completed
+  # (#use_completion_button?).
   class Rendering
 
-    MAPPING = [%w{perform_url perform_url}, %w{details_url details_url}, 
%w{form_completing completing}]
-    attr_reader :perform_url, :details_url, :completing
+    MAPPING = %w{perform_url details_url integrated_ui}.map { |name| [name, 
name] }
+    attr_reader :perform_url, :details_url, :integrated_ui
 
-    def initialize(perform_url, details_url, completing)
+    def initialize(perform_url, details_url, integrated_ui)
       @perform_url = perform_url
       @details_url = details_url if perform_url
-      @completing = perform_url && completing || false
+      @integrated_ui = (perform_url && integrated_ui) || false
     end
 
-    # True if rendering the task using only the task description.
-    def use_description?(performing)
-      performing ? perform_url.nil? : details_url.nil?
+    # True if rendering a button for user to mark task as completed.
+    def use_completion_button?
+      !perform_url || !integrated_ui
     end
 
-    # True if we need to include button to mark task as completed.
-    def use_completion_button?
-      !perform_url || !completing
+    # Returns most suitable URL for rendering the task.
+    #
+    # Returns nil if there is no suitable URL for rendering the task,
+    # otherwise, returns perform_url or details_url.  If the integrated_ui
+    # option is available, passes query parameters to the rendered URL.  Query
+    # parameters are passed as last argument or returned from the block.
+    def render_url(perform, params = {})
+      url = perform ? perform_url : details_url
+      return url unless integrated_ui
+      params = yield if block_given?
+      uri = URI(url)
+      uri.query = CGI.parse(uri.query || '').update(params).to_query
+      uri.to_s
     end
 
   end
 
   composed_of :rendering, :class_name=>Rendering.to_s, 
:mapping=>Rendering::MAPPING do |hash|
-    Rendering.new(hash[:perform_url], hash[:details_url], hash[:completing])
+    Rendering.new(hash[:perform_url], hash[:details_url], hash[:integrated_ui])
   end
 
   validates_url :perform_url, :allow_nil=>true
@@ -288,7 +302,7 @@
   end
 
   def over_due?
-    (ready? || active?) && due_on && due_on < Date.today
+    due_on ? (ready? || active?) && due_on < Date.today : false
   end
 
   # Scopes can use this to add ranking methods on returned records.

Modified: ode/sandbox/singleshot/app/views/tasks/show.html.erb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/app/views/tasks/show.html.erb?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/app/views/tasks/show.html.erb (original)
+++ ode/sandbox/singleshot/app/views/tasks/show.html.erb Thu Jun  5 16:26:42 
2008
@@ -1,6 +1,10 @@
 <%
   performing = @task.can_complete?(authenticated)
-  use_description = @task.rendering.use_description?(performing)
+  iframe_url = @task.rendering.render_url(performing) {
+    uri = URI(task_perform_url(@task))
+    uri.user, uri.password = '_token', @task.token_for(authenticated)
+    { 'task_url'=>uri.to_s }
+  }
 %>
 <% div_for @task do %>
   <div class='header'>
@@ -16,7 +20,7 @@
     </div>
     <div class='details' style='display:none'>
       <dl>
-        <%= content_tag('dt', 'Description') + content_tag('dd', 
sanitize(simple_format(@task.description))) unless use_description %>
+        <%= content_tag('dt', 'Description') + content_tag('dd', 
sanitize(simple_format(@task.description))) if iframe_url %>
         <dt>Priority</dt><dd><%= ['High', 'Medium', 'Low'[EMAIL PROTECTED] - 
1] %></dd>
         <%= content_tag('dt', 'Due on') + content_tag('dd', 
relative_date(@task.due_on).humanize) if @task.due_on %>
         <dt>Recent activity</dt>
@@ -29,10 +33,10 @@
     </div>
   </div>
 
-  <% if use_description %>
-    <div class='description'><%= sanitize(simple_format(@task.description)) 
%></div>
-  <% else %>
-    <%= task_frame @task, performing %>
+  <% if iframe_url %>
+    <iframe id='task_frame' src='<%= iframe_url %>'></iframe>
     <%= javascript_tag "Singleshot.setFrameSize('task_frame')" %>
+  <% else %>
+    <div class='description'><%= sanitize(simple_format(@task.description)) 
%></div>
   <% end %>
 <% end %>

Modified: ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb (original)
+++ ode/sandbox/singleshot/db/migrate/20080506015046_create_tasks.rb Thu Jun  5 
16:26:42 2008
@@ -8,7 +8,7 @@
       t.string    'status',       :null=>false
       t.string    'perform_url'
       t.string    'details_url'
-      t.boolean   'form_completing'
+      t.boolean   'integrated_ui'
       t.string    'outcome_url',  :null=>true
       t.string    'outcome_type', :null=>true
       t.string    'access_key',   :null=>true, :limit=>32

Modified: ode/sandbox/singleshot/db/schema.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/db/schema.rb?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/db/schema.rb (original)
+++ ode/sandbox/singleshot/db/schema.rb Thu Jun  5 16:26:42 2008
@@ -43,19 +43,19 @@
   end
 
   create_table "tasks", :force => true do |t|
-    t.string   "title",                                        :null => false
-    t.string   "description",                                  :null => false
-    t.integer  "priority",        :limit => 1,                 :null => false
+    t.string   "title",                                      :null => false
+    t.string   "description",                                :null => false
+    t.integer  "priority",      :limit => 1,                 :null => false
     t.date     "due_on"
-    t.string   "status",                                       :null => false
+    t.string   "status",                                     :null => false
     t.string   "perform_url"
     t.string   "details_url"
-    t.boolean  "form_completing"
+    t.boolean  "integrated_ui"
     t.string   "outcome_url"
     t.string   "outcome_type"
-    t.string   "access_key",      :limit => 32
-    t.text     "data",                                         :null => false
-    t.integer  "version",                       :default => 0, :null => false
+    t.string   "access_key",    :limit => 32
+    t.text     "data",                                       :null => false
+    t.integer  "version",                     :default => 0, :null => false
     t.datetime "created_at"
     t.datetime "updated_at"
   end

Modified: ode/sandbox/singleshot/lib/tasks/populate.rake
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/lib/tasks/populate.rake?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/lib/tasks/populate.rake (original)
+++ ode/sandbox/singleshot/lib/tasks/populate.rake Thu Jun  5 16:26:42 2008
@@ -35,7 +35,7 @@
       Task.delay 
       you = Person.find_by_identity(ENV['USER']) 
       defaults = { :title=>Faker::Lorem.sentence, 
:description=>Faker::Lorem.paragraphs(3).join("\n\n"),
-                   :rendering=>{ 
:perform_url=>'http://localhost:3001/sandwich', :completing=>true }, 
:potential_owners=>[you, other] }
+                   :rendering=>{ 
:perform_url=>'http://localhost:3001/sandwich', :integrated_ui=>true }, 
:potential_owners=>[you, other] }
       returning Task.new(defaults.merge(attributes || {})) do |task|
         task.modify_by(you).save!
         def task.delay(duration = 2.hours)

Modified: ode/sandbox/singleshot/public/stylesheets/default.css
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/public/stylesheets/default.css?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/public/stylesheets/default.css (original)
+++ ode/sandbox/singleshot/public/stylesheets/default.css Thu Jun  5 16:26:42 
2008
@@ -54,7 +54,7 @@
 }
 form.button-to input, a.button-to, button {
   font-size: 1em;
-  font-weight: bold;
+  font-weight: normal;
   border: none;
   padding: 0.3em 0.6em 0.3em 0.6em;
   margin: 0;
@@ -109,10 +109,11 @@
   border-bottom: solid 2px #055da4;
 }
 #header h1 {
-  font-size: 2.2em;
+  font-size: 2.0em;
   font-weight: bold;
-  margin: 0 auto 0.7em auto;
+  margin: 0 auto 0.6em auto;
   float: left;
+  letter-spacing: -1pt;
 }
 #header h1 a {
   color: #404040;
@@ -125,8 +126,6 @@
 }
 #header ul.links li {
   display: inline;
-  margin: 0;
-  padding: 0.5em;
 }
 #header ul.links li a {
   text-decoration: none;
@@ -146,20 +145,19 @@
 }
 #header ul.tabs li a {
   color: #eee;
-  font-size: 1.2em;
-  font-weight: bold;
+  font-size: 1.1em;
   text-decoration: none;
   background-color: #0081d1; 
   padding: 0.3em 0.9em 0.3em 0.9em;
   -moz-border-radius: 4px 4px 0 0;
-  -webkit-border-top-left-radius: 6px;
-  -webkit-border-top-right-radius: 6px;
+  -webkit-border-top-left-radius: 4px;
+  -webkit-border-top-right-radius: 4px;
 }
 #header ul.tabs li a:hover, #header ul.tabs li a.current {
   background-color: #055da4;
 }
 #header ul.alternate {
-  margin-top: -1.3em;
+  margin-top: -1.5em;
 }
 
 
@@ -294,10 +292,11 @@
   border-bottom: solid 2px #055da4;
 }
 div.task div.header h1 {
-  display: inline;
+  font-size: 2.0em;
+  font-weight: bold;
+  margin: 0.2em 1em 0 1.5em;
   float: left;
-  font-size: 1.5em;
-  margin: 0.6em 2em 0 2.0em;
+  letter-spacing: -1pt;
 }
 div.task div.header h1 a {
   color: #404040;
@@ -307,9 +306,10 @@
   white-space: nowrap;
   text-overflow: ellipsis;
   overflow: hidden;
+  margin: 0.6em auto 0.6em auto;
 }
 div.task div.header div.vitals p {
-  margin: 0.3em 0 0.3em 0;
+  margin: 0 auto 0.3em auto;
   text-overflow: ellipsis;
   overflow: hidden;
 }
@@ -404,6 +404,3 @@
 form.login input {
   width: 100%;
 }
-
-
-

Modified: ode/sandbox/singleshot/spec/models/task_spec.rb
URL: 
http://svn.apache.org/viewvc/ode/sandbox/singleshot/spec/models/task_spec.rb?rev=663778&r1=663777&r2=663778&view=diff
==============================================================================
--- ode/sandbox/singleshot/spec/models/task_spec.rb (original)
+++ ode/sandbox/singleshot/spec/models/task_spec.rb Thu Jun  5 16:26:42 2008
@@ -273,19 +273,25 @@
   describe 'over_due?' do
 
     it 'should be false if task has no due date' do
-      Task.new.over_due?.should be_false
+      Task.create(defaults).over_due?.should be_false
     end
 
     it 'should be false if task due date in the future' do
-      Task.new(:due_on=>Date.tomorrow).over_due?.should be_false
+      Task.create(defaults(:due_on=>Date.tomorrow)).over_due?.should be_false
     end
 
     it 'should be false if task due today' do
-      Task.new(:due_on=>Date.today).over_due?.should be_false
+      Task.create(defaults(:due_on=>Date.today)).over_due?.should be_false
     end
 
     it 'should be true if task due date in the past' do
-      Task.new(:due_on=>Date.yesterday).over_due?.should be_true
+      Task.create(defaults(:due_on=>Date.yesterday)).over_due?.should be_true
+    end
+
+    it 'should be true only if task is ready or active' do
+      for status in Task::STATUSES
+        task_with_status(status, :due_on=>Date.yesterday).over_due?.should == 
(status == 'ready' || status == 'active')
+      end
     end
 
   end
@@ -331,6 +337,117 @@
 end
 
 
+describe Task::Rendering do
+  it 'should store perform_url attribute' do
+    Task.create! defaults(:perform_url=>'http://foobar/')
+    Task.last.rendering.perform_url.should == 'http://foobar/'
+  end
+
+  it 'should store details_url attribute' do
+    Task.create! defaults(:perform_url=>'http://foobar/', 
:details_url=>'http://barfoo/')
+    Task.last.rendering.details_url.should == 'http://barfoo/'
+  end
+
+  it 'should not have details_url without perform_url' do
+    Task.create! defaults(:details_url=>'http://barfoo/')
+    Task.last.rendering.details_url.should be_nil
+  end
+
+  it 'should store integrated_ui attribute' do
+    Task.create! defaults(:perform_url=>'http://foobar/', :integrated_ui=>true)
+    Task.last.rendering.integrated_ui.should be_true
+  end
+
+  it 'should not have integrated_ui without perform_url' do
+    Task.create! defaults(:integrated_ui=>true)
+    Task.last.rendering.integrated_ui.should be_false
+  end
+
+  it 'should default integrated_ui attribute to false' do
+    Task.create! defaults(:perform_url=>'http://foobar/')
+    Task.last.rendering.integrated_ui.should be_false
+  end
+
+  it 'should use completion button if no perform_url' do
+    Task.create! defaults
+    Task.last.rendering.use_completion_button?.should be_true
+  end
+
+  it 'should use completion button if perform_url but no integrated_ui' do
+    Task.create! defaults(:perform_url=>'http://foobar/')
+    Task.last.rendering.use_completion_button?.should be_true
+  end
+
+  it 'should not use completion button if perform_url and integrated_ui' do
+    Task.create! defaults(:perform_url=>'http://foobar/', :integrated_ui=>true)
+    Task.last.rendering.use_completion_button?.should be_false
+  end
+
+  it 'should have nil render_url without perform_url' do
+    Task.new.rendering.render_url(true).should be_nil
+  end
+
+  it 'should render using perform_url when performing task' do
+    Task.new(:perform_url=>'http://foobar/', 
:details_url=>'http://barfoo/').rendering.render_url(true).should == 
'http://foobar/'
+  end
+
+  it 'should render using perform_url with query parameter when performing 
integrated task' do
+    task = Task.new(:perform_url=>'http://foobar/', 
:details_url=>'http://barfoo/', :integrated_ui=>true)
+    task.rendering.render_url(true, 'url'=>'http://test.host').should == 
"http://foobar/?url=#{CGI.escape('http://test.host')}"
+    task.rendering.render_url(true) { { 'url'=>'http://test.host' } }.should 
== "http://foobar/?url=#{CGI.escape('http://test.host')}"
+  end
+
+  it 'should have nil render_url without details_url' do
+    
Task.new(:perform_url=>'http://foobar/').rendering.render_url(false).should 
be_nil
+  end
+
+  it 'should render using details_url when performing task' do
+    Task.new(:perform_url=>'http://foobar/', 
:details_url=>'http://barfoo/').rendering.render_url(false).should == 
'http://barfoo/'
+  end
+
+  it 'should render using details_url with query parameters when viewing 
integrated task' do
+    task = Task.new(:perform_url=>'http://foobar/', 
:details_url=>'http://barfoo/', :integrated_ui=>true)
+    task.rendering.render_url(false, 'url'=>'http://test.host' ).should == 
"http://barfoo/?url=#{CGI.escape('http://test.host')}"
+    task.rendering.render_url(false) { { 'url'=>'http://test.host' } }.should 
== "http://barfoo/?url=#{CGI.escape('http://test.host')}"
+  end
+
+  it 'should be assignable from hash' do
+    hash = { :perform_url=>'http://foobar/', :details_url=>'http://barfoo/', 
:integrated_ui=>true }
+    task = Task.new
+    task.attributes = { :rendering=>hash }
+    hash.each do |key, value|
+      task.rendering.send(key).should == value
+    end
+  end
+
+  it 'should validate URLs' do
+    Task.new(:perform_url=>'http://+++').should have(1).error_on(:perform_url)
+    Task.new(:details_url=>'http://+++').should have(1).error_on(:details_url)
+  end
+
+  it 'should allow HTTP URLS' do
+    Task.new(:perform_url=>'http://test.host/foo').should have(:no).errors
+    Task.new(:details_url=>'http://test.host?foo=bar').should have(:no).errors
+  end
+
+  it 'should allow HTTPS URLS' do
+    Task.new(:perform_url=>'https://test.host/foo').should have(:no).errors
+    Task.new(:details_url=>'https://test.host?foo=bar').should have(:no).errors
+  end
+
+  it 'should not allow other URL schemes' do
+    Task.new(:perform_url=>'ftp://test.host/foo').should 
have(1).error_on(:perform_url)
+    Task.new(:details_url=>'file:///var/log').should 
have(1).error_on(:details_url)
+  end
+
+  it 'should store normalized URLs' do
+    Task.create defaults(:perform_url=>'HTTP://Test.Host/Foo', 
:details_url=>'HTTPS://Foo:[EMAIL PROTECTED]')
+    Task.last.perform_url.should eql('http://test.host/Foo')
+    Task.last.details_url.should eql('https://Foo:[EMAIL PROTECTED]/?Foo=Bar')
+  end
+
+end
+
 =begin
 
 describe Task, 'url', :shared=>true do


Reply via email to