myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [3224] branches/components: Merged snapshots bran


From: noreply
Subject: [myexperiment-hackers] [3224] branches/components: Merged snapshots branch
Date: Wed, 5 Dec 2012 15:06:12 +0000 (UTC)

Revision
3224
Author
fbacall
Date
2012-12-05 15:06:12 +0000 (Wed, 05 Dec 2012)

Log Message

Merged snapshots branch

Modified Paths

Added Paths

Diff

Modified: branches/components/app/controllers/packs_controller.rb (3223 => 3224)


--- branches/components/app/controllers/packs_controller.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/app/controllers/packs_controller.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -403,6 +403,24 @@
     end
   end
   
+  def snapshot
+
+    success = @pack.snapshot!
+
+    respond_to do |format|
+      format.html {
+        if success
+          @pack.reload
+          flash[:notice] = 'Pack snapshot was successfully created.'
+          redirect_to pack_version_path(@pack, @pack.versions.last.version)
+        else
+          flash[:error] = 'There was a problem with creating the snapshot.'
+          redirect_to pack_path(@pack)
+        end
+      }
+    end
+  end
+
   protected
   
   # Check that a protocol is specified in the URI; prepend HTTP:// otherwise
@@ -438,7 +456,8 @@
       "statistics"       => "view",
       "tag"              => "view",
       "update"           => "edit",
-      "update_item"      => "edit"
+      "update_item"      => "edit",
+      "snapshot"         => "edit"
     }
 
     begin
@@ -447,6 +466,8 @@
       if Authorization.check(action_permissions[action_name], pack, current_user)
         @pack = pack
         
+        @version = @pack.find_version(params[:version]) if params[:version]
+
         @authorised_to_edit = logged_in? && Authorization.check("edit", @pack, current_user)
         @authorised_to_download = Authorization.check("download", @pack, current_user)
         

Modified: branches/components/app/models/pack.rb (3223 => 3224)


--- branches/components/app/models/pack.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/app/models/pack.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -24,6 +24,15 @@
 
   has_many :relationships, :dependent => :destroy, :as => :context
 
+  has_many :versions, :class_name => "PackVersion"
+
+  def find_version(version)
+    match = versions.find(:first, :conditions => ["version = ?", version])
+    return match if match
+
+    raise ActiveRecord::RecordNotFound.new("Couldn't find Pack with pack_id=#{id} and version=#{version}")
+  end
+
   validates_presence_of :title
   
   format_attribute :description
@@ -35,17 +44,19 @@
   has_many :contributable_entries,
            :class_name => "PackContributableEntry",
            :foreign_key => :pack_id,
+           :conditions => "version IS NULL",
            :order => "created_at DESC",
            :dependent => :destroy
   
   has_many :remote_entries,
            :class_name => "PackRemoteEntry",
            :foreign_key => :pack_id,
+           :conditions => "version IS NULL",
            :order => "created_at DESC",
            :dependent => :destroy
   
   def items_count
-    return contributable_entries.count + remote_entries.count
+    contributable_entries.count + remote_entries.count
   end
   
   # returns packs that have largest total number of items
@@ -631,6 +642,58 @@
     APIStatistics.statistics(self)
   end
  
+  def snapshot!
+  
+    self.current_version = self.current_version ? self.current_version + 1 : 1
+
+    inhibit_timestamps do
+ 
+      version = versions.build(
+          :version          => current_version,
+          :contributor      => contributor,
+          :title            => title,
+          :description      => description,
+          :description_html => description_html)
+
+      contributable_entries.each do |entry|
+
+        version.contributable_entries.build(
+            :pack => self,
+            :contributable_id => entry.contributable_id,
+            :contributable_type => entry.contributable_type,
+            :contributable_version => entry.contributable_version,
+            :comment => entry.comment,
+            :user_id => entry.user_id,
+            :version => current_version,
+            :created_at => entry.created_at,
+            :updated_at => entry.updated_at)
+      end
+
+      remote_entries.each do |entry|
+
+        tt = version.remote_entries.build(
+            :pack => self,
+            :title => entry.title,
+            :uri => entry.uri,
+            :alternate_uri => entry.alternate_uri,
+            :comment => entry.comment,
+            :user_id => entry.user_id,
+            :version => current_version,
+            :created_at => entry.created_at,
+            :updated_at => entry.updated_at)
+      end
+    end
+    
+    save
+  end
+
+  def describe_version(version_number)
+    return "" if versions.count < 2
+    return "(earliest)" if version_number == versions.first.version
+    return "(latest)" if version_number == versions.last.version
+    return ""
+  end
+
   protected
   
   # produces html string containing the required messaged, enclosed within left-padded P tag, belonging to 'none_text' class

Modified: branches/components/app/models/pack_contributable_entry.rb (3223 => 3224)


--- branches/components/app/models/pack_contributable_entry.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/app/models/pack_contributable_entry.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -21,12 +21,26 @@
   after_destroy :touch_pack
 
   def check_unique
-    if self.contributable_version.blank?
-      i = PackContributableEntry.find(:first, :conditions => ["pack_id = ? AND contributable_type = ? AND contributable_id = ? AND contributable_version IS NULL", self.pack_id, self.contributable_type, self.contributable_id]) 
+
+    conditions = ["pack_id = ?", "version = ?", "contributable_type = ?", "contributable_id = ?"]
+    arguments = [self.pack_id, self.version, self.contributable_type, self.contributable_id]
+    
+    if self.contributable_version.nil?
+      conditions << "contributable_version IS NULL"
     else
-      i = PackContributableEntry.find(:first, :conditions => ["pack_id = ? AND contributable_type = ? AND contributable_id = ? AND contributable_version = ?", self.pack_id, self.contributable_type, self.contributable_id, self.contributable_version])
+      conditions << "contributable_version = ?"
+      arguments << self.contributable_version
     end
-    
+
+    if self.version.nil?
+      conditions << "version IS NULL"
+    else
+      conditions << "version = ?"
+      arguments << self.version
+    end
+
+    i = PackContributableEntry.find(:first, :conditions => [conditions.join(" AND ")] + arguments) 
+ 
     if i
       errors.add_to_base("This item already exists in the pack")
       return false

Modified: branches/components/app/models/pack_remote_entry.rb (3223 => 3224)


--- branches/components/app/models/pack_remote_entry.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/app/models/pack_remote_entry.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -19,7 +19,7 @@
   after_destroy :touch_pack
 
   def check_unique
-    if PackRemoteEntry.find(:first, :conditions => ["pack_id = ? AND uri = ?", self.pack_id, self.uri])
+    if PackRemoteEntry.find(:first, :conditions => ["pack_id = ? AND version = ? AND uri = ?", self.pack_id, self.version, self.uri])
       errors.add_to_base("This external link already exists in the pack")
       return false
     else

Copied: branches/components/app/models/pack_version.rb (from rev 3223, branches/snapshots/app/models/pack_version.rb) (0 => 3224)


--- branches/components/app/models/pack_version.rb	                        (rev 0)
+++ branches/components/app/models/pack_version.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -0,0 +1,50 @@
+# myExperiment: app/models/pack_version.rb
+#
+# Copyright (c) 2012 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class PackVersion < ActiveRecord::Base
+
+  validates_presence_of :title
+
+  belongs_to :pack
+  belongs_to :contributor, :polymorphic => true
+
+  format_attribute :description
+
+  has_many :contributable_entries,
+           :class_name => "PackContributableEntry",
+           :dependent => :destroy,
+           :finder_sql =>
+              'SELECT *
+               FROM pack_contributable_entries
+               WHERE pack_id = #{pack_id} AND version = #{version}
+               ORDER BY created_at DESC'
+  
+  has_many :remote_entries,
+           :class_name => "PackRemoteEntry",
+           :dependent => :destroy,
+           :finder_sql =>
+              'SELECT *
+               FROM pack_remote_entries
+               WHERE pack_id = #{pack_id} AND version = #{version}
+               ORDER BY created_at DESC'
+
+  def items_count
+    contributable_entries.count + remote_entries.count
+  end
+  
+  def versioned_resource
+    pack
+  end
+
+  def items_count
+    return contributable_entries.count + remote_entries.count
+  end
+
+  def contributables
+    contributable_entries.map do |e| e.contributable end
+  end
+  
+end
+

Modified: branches/components/app/views/packs/_items.rhtml (3223 => 3224)


--- branches/components/app/views/packs/_items.rhtml	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/app/views/packs/_items.rhtml	2012-12-05 15:06:12 UTC (rev 3224)
@@ -10,7 +10,7 @@
 		<ul id="packItemsList">
 			
 			<!-- Contributable Item Entries -->
-			<% pack.contributable_entries.each do |e| %>
+			<% contributable_entries.each do |e| %>
 				<% show = e.available? ? Authorization.check("view", e.contributable, current_user) : false -%>
 				<li>
 					<table>
@@ -98,7 +98,7 @@
 			<% end -%>
 			
 			<!-- Remote Item Entries -->
-			<% pack.remote_entries.each do |e| %>
+			<% remote_entries.each do |e| %>
 				<li>
 					<table>
 						<tr>

Copied: branches/components/app/views/packs/_version_selector.rhtml (from rev 3223, branches/snapshots/app/views/packs/_version_selector.rhtml) (0 => 3224)


--- branches/components/app/views/packs/_version_selector.rhtml	                        (rev 0)
+++ branches/components/app/views/packs/_version_selector.rhtml	2012-12-05 15:06:12 UTC (rev 3224)
@@ -0,0 +1,82 @@
+<script type="text/_javascript_">
+  function showVersion(form) {
+    var url = ""
+		location.href = ""
+		form.submit
+  }
+</script>
+
+<div class="contribution_version_selector_box">
+
+  <table>
+    <tbody>
+      <tr>
+        <td class="heading" style="vertical-align: top;">
+          <% if version %>
+            <%= info_icon_with_tooltip("This box shows version #{version.version.to_s} for this entry") -%>
+            <span><%= "Version #{version.version.to_s} #{resource.describe_version(version.version)}" -%></span>
+            <span class="count_text">(of <%= resource.versions.length -%>)</span>
+          <% else %>
+            <%= info_icon_with_tooltip("This box shows the live version for this entry") -%>
+            <span>Live view</span>
+            <% if resource.versions.length > 0 %>
+              <span class="count_text">(<%= resource.versions.length -%> versions available)</span>
+            <% end %>
+          <% end %>
+          <a name="versions"></a>
+        </td>
+        <td>
+          <% if resource.versions.length > 0 %>
+             <form  return false;" style="text-align: right;">
+              <b>View version: </b>
+              <select id="resource_versions" 
+                <option value="<%= polymorphic_path(resource) %>" <%= "selected" if version.nil? -%>>Live view</option>
+                <% resource.versions.reverse.each do |v| %>
+                  <option value="<%= send(path, resource, v.version.to_s) %>" <%= "selected" if !version.nil? && v.version == version.version -%>>
+                      <%= "#{v.version.to_s} #{resource.describe_version(v.version)}" %>
+                  </option>
+                <% end %>
+              </select>
+            </form>
+          <% end %>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+      
+  <% if version %>
+    <div id="version_info_box" style="color: #666666;  font-size: 85%; margin: 0.6em 0.5em 0.2em 0.5em; border-top: 1px solid #DDDDDD; padding-top: 0.4em;">
+      <p style="text-align: center;">
+        <b>Version created on:</b>
+        <span><%= datetime version.created_at, false %></span>
+        <% if version.respond_to?(:contributor_id) && version.respond_to?(:contributor_type) %>
+          <b>by:</b>
+          <span><%= contributor(version.contributor_id, version.contributor_type) %></span>
+        <% end %>
+        <% if !version.revision_comments.blank? %>
+          <span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
+          <span><%= link_to_function "Revision comment " + expand_image, visual_effect(:toggle_blind, "version_info_box_comments", :duration => 0.3) %></span>
+        <% end %>
+      </p>
+      
+      <% unless version.created_at == version.updated_at %>
+        <p style="text-align: center;">
+          <b>Last edited on:</b>
+          <span><%= datetime version.updated_at, false %></span>
+          <% if version.respond_to?(:last_edited_by) %>
+            <b>by:</b>
+            <span><%= contributor(version.last_edited_by, "User") %></span>
+          <% end %>
+        </p>
+      <% end %>
+    </div>
+  <% end %>
+  
+  <% if version && !version.revision_comments.blank? -%>
+    <div id="version_info_box_comments" style="display: none; border: 1px dotted #CCCCCC; padding: 0.3em 0.5em;">
+      <%= white_list version.revision_comments %>
+    </div>
+  <% end %>
+
+</div>
+

Modified: branches/components/app/views/packs/show.rhtml (3223 => 3224)


--- branches/components/app/views/packs/show.rhtml	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/app/views/packs/show.rhtml	2012-12-05 15:06:12 UTC (rev 3224)
@@ -1,10 +1,17 @@
 <% t "#{contributable_name(@pack.id, 'Pack')} (#{h @pack.contributor_name})" -%>
 
+<% items_count = @version ? @version.items_count : @pack.items_count %>
+<% contributable_entries = @version ? @version.contributable_entries : @pack.contributable_entries %>
+<% remote_entries        = @version ? @version.remote_entries        : @pack.remote_entries        %>
+
 <% if @authorised_to_edit %>
 	<ul class="sectionIcons">
 		<% if mine?(@pack) -%>
 			<li><%= icon('manage', edit_pack_path(@pack), nil, nil, 'Manage Pack') -%></li>
 		<% end -%>
+    <% if @authorised_to_edit -%>
+			<li><%= icon('new', snapshot_pack_path(@pack), nil, { :confirm => 'Are you sure that you would like to create a new snapshot of this Pack?', :method => :post }, 'Create snapshot') %></li>
+    <% end -%>	
 		<% if Authorization.check("destroy", @pack, current_user) %>
 			<li><%= icon('destroy', pack_path(@pack), nil, { :confirm => 'This deletes the Pack and all metadata such as tags and comments, BUT does not delete the actual items pointed to in the Pack. Are you sure you would like to delete this Pack?', :method => :delete }, 'Delete Pack') %></li>
 		<% end %>
@@ -37,6 +44,9 @@
 
 <div class="contribution_left_box">
 	<div class="contribution_version_box">
+
+    <%= render(:partial => "packs/version_selector", :locals => { :resource => @pack, :version => @version, :path => :pack_version_path }) %>
+
 		<div class="contribution_version_inner_box">
 			<p>
 		    <b>Title:</b>
@@ -76,10 +86,10 @@
 				<% end %>
 				
 				<%= info_icon_with_tooltip("This section shows all the items that are pointed to in this pack. This can be a combination of internal and external items.") -%>
-				Items <span class="count_text">(<%= @pack.items_count -%>)</span>
+				Items <span class="count_text">(<%= items_count -%>)</span>
 			</h4>
 			
-			<%= render :partial => "items", :locals => { :pack => @pack, :authorised_to_edit => @authorised_to_edit } -%>
+			<%= render :partial => "items", :locals => { :pack => @pack, :contributable_entries => contributable_entries, :remote_entries => remote_entries, :authorised_to_edit => @authorised_to_edit } -%>
 
 			<br/><br/>
 			<h4>
@@ -133,7 +143,7 @@
 	
 	<div class="contribution_section_box">
 		<p style="font-size: 108%;">
-		 	<b><%= pluralize @pack.items_count, "item" %> in this pack</b>
+		 	<b><%= pluralize items_count, "item" %> in this pack</b>
 		</p>
 	</div>
 	

Modified: branches/components/config/routes.rb (3223 => 3224)


--- branches/components/config/routes.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/config/routes.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -99,6 +99,7 @@
                  :download => :get,
                  :quick_add => :post,
                  :resolve_link => :post,
+                 :snapshot => :post,
                  :items => :get } do |pack|
     pack.resources :comments, :collection => { :timeline => :get }
     pack.resources :relationships, :collection => { :edit_relationships => :get }
@@ -136,6 +137,10 @@
   map.blob_version           '/files/:id/versions/:version',         :conditions => { :method => :get }, :controller => 'blobs', :action ="" 'show'
   map.formatted_blob_version '/files/:id/versions/:version.:format', :conditions => { :method => :get }, :controller => 'blobs', :action ="" 'show'
 
+  # pack redirect for linked data model
+  map.pack_version           '/packs/:id/versions/:version',         :conditions => { :method => :get }, :controller => 'packs', :action ="" 'show'
+  map.formatted_pack_version '/packs/:id/versions/:version.:format', :conditions => { :method => :get }, :controller => 'packs', :action ="" 'show'
+
   map.blob_version_suggestions '/files/:id/versions/:version/suggestions', :conditions => { :method => :get }, :controller => 'blobs', :action ="" 'suggestions'
   map.blob_version_process_suggestions '/files/:id/versions/:version/process_suggestions', :conditions => { :method => :post }, :controller => 'blobs', :action ="" 'process_suggestions'
 

Modified: branches/components/config/schema.d/packs.xml (3223 => 3224)


--- branches/components/config/schema.d/packs.xml	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/config/schema.d/packs.xml	2012-12-05 15:06:12 UTC (rev 3224)
@@ -10,6 +10,7 @@
     <column type="text"     name="description_html"/>
     <column type="datetime" name="created_at"/>
     <column type="datetime" name="updated_at"/>
+    <column type="integer"  name="current_version"/>
 
   </table>
 
@@ -23,6 +24,7 @@
     <column type="integer"  name="user_id"               null="false"/>
     <column type="datetime" name="created_at"/>
     <column type="datetime" name="updated_at"/>
+    <column type="integer"  name="version"/>
 
   </table>
 
@@ -36,6 +38,7 @@
     <column type="integer"  name="user_id"       null="false"/>
     <column type="datetime" name="created_at"/>
     <column type="datetime" name="updated_at"/>
+    <column type="integer"  name="version"/>
 
   </table>
 

Modified: branches/components/config/tables.xml


(Binary files differ)

Copied: branches/components/db/migrate/098_create_pack_versions.rb (from rev 3223, branches/snapshots/db/migrate/098_create_pack_versions.rb) (0 => 3224)


--- branches/components/db/migrate/098_create_pack_versions.rb	                        (rev 0)
+++ branches/components/db/migrate/098_create_pack_versions.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -0,0 +1,33 @@
+# myExperiment: db/migrate/097_create_pack_versions.rb
+#
+# Copyright (c) 2012 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class CreatePackVersions < ActiveRecord::Migration
+
+  def self.up
+    create_table :pack_versions do |t|
+      t.integer  "pack_id"
+      t.integer  "version"
+      t.text     "revision_comments"
+      t.string   "title"
+      t.text     "description"
+      t.text     "description_html"
+      t.datetime "created_at"
+      t.datetime "updated_at"
+    end
+
+    add_column :packs, :current_version, :integer
+    add_column :pack_contributable_entries, :version, :integer
+    add_column :pack_remote_entries, :version, :integer
+  end
+
+  def self.down
+    remove_column :packs, :current_version
+    remove_column :pack_contributable_entries, :version
+    remove_column :pack_remote_entries, :version
+
+    drop_table :pack_versions
+  end
+end
+

Modified: branches/components/db/schema.rb (3223 => 3224)


--- branches/components/db/schema.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/db/schema.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -431,6 +431,7 @@
     t.integer  "user_id",               :null => false
     t.datetime "created_at"
     t.datetime "updated_at"
+    t.integer  "version"
   end
 
   create_table "pack_remote_entries", :force => true do |t|
@@ -442,8 +443,20 @@
     t.integer  "user_id",       :null => false
     t.datetime "created_at"
     t.datetime "updated_at"
+    t.integer  "version"
   end
 
+  create_table "pack_versions", :force => true do |t|
+    t.integer  "pack_id"
+    t.integer  "version"
+    t.text     "revision_comments"
+    t.string   "title"
+    t.text     "description"
+    t.text     "description_html"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
   create_table "packs", :force => true do |t|
     t.integer  "contributor_id"
     t.string   "contributor_type"
@@ -452,6 +465,7 @@
     t.text     "description_html"
     t.datetime "created_at"
     t.datetime "updated_at"
+    t.integer  "current_version"
   end
 
   create_table "pending_invitations", :force => true do |t|

Modified: branches/components/lib/authorization.rb (3223 => 3224)


--- branches/components/lib/authorization.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/lib/authorization.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -85,6 +85,24 @@
             return true
         end
       
+      when "PackVersion"
+        case action
+          when "create"
+
+            # If a user can edit a pack, they can create a version of it.
+            is_authorized = Authorization.check('edit', context, user)
+
+          when "view"
+
+            # If a user can view a pack, they can view versions of it.
+            is_authorized = Authorization.check('view', context, user)
+
+          else
+            
+            # Editing or deleting versions of a pack is not allowed.
+            is_authorized = false
+        end
+
       when "Comment"
         case action
           when "create"

Modified: branches/components/lib/rest.rb (3223 => 3224)


--- branches/components/lib/rest.rb	2012-12-05 14:32:27 UTC (rev 3223)
+++ branches/components/lib/rest.rb	2012-12-05 15:06:12 UTC (rev 3224)
@@ -190,7 +190,11 @@
           list_element[key] = value
         end
 
-        collection = eval("ob.#{model_data['Accessor'][i]}")
+        if query['version'] and model_data['Versioned'][i] == 'yes'
+          collection = eval(sprintf("ob.find_version(%d).%s", query['version'], model_data['Accessor'][i]))
+        else
+          collection = eval("ob.#{model_data['Accessor'][i]}")
+        end
 
         collection = [collection] if model_data['Encoding'][i] == 'item as list'
 
@@ -341,7 +345,7 @@
 end
 
 def find_entity_name_from_object(ob)
-  ob = ob.versioned_resource if ob.respond_to?(:version)
+  ob = ob.versioned_resource if ob.respond_to?(:versioned_resource)
   OBJECT_CLASS_TO_ENTITY_NAME[ob.class.name.underscore]
 end
 
@@ -383,6 +387,7 @@
   if query['version']
     return rest_response(400, :reason => "Object does not support versioning") unless ob.respond_to?('versions')
     return rest_response(404, :reason => "Specified version does not exist") if query['version'].to_i < 1
+    return rest_response(404, :reason => "Specified version does not exist") if query['version'].to_i > ob.versions.last.version
   end
 
   # Work out which elements to include in the response.
@@ -683,6 +688,7 @@
     when 'Ontology';               return ontology_url(ob)
     when 'Predicate';              return predicate_url(ob)
     when 'Relationship';           return nil
+    when 'PackVersion';            return pack_version_url(ob, ob.version)
 
     when 'Creditation';     return nil
     when 'Attribution';     return nil
@@ -733,6 +739,7 @@
     when 'Attribution';     return nil
 
     when 'WorkflowVersion'; return "#{base}/workflow.xml?id=#{ob.workflow.id}&version=#{ob.version}"
+    when 'PackVersion';     return "#{base}/pack.xml?id=#{ob.pack.id}&version=#{ob.version}"
   end
 
   raise "Class not processed in rest_access_uri: #{ob.class.to_s}"

reply via email to

[Prev in Thread] Current Thread [Next in Thread]