Description

This class in an abstraction layer to the attachments/ folder. It allows access to the folder content in a way that mimics the working of ActiveRecord

The Attachment class inherits from the ruby core File class

Folder structure The attachement folder structure example: AttachmentPWD

   |
   - 1     - this directory level represents the nodes, folder name = node id
   |   |
   |   - 3.image.gif
   |   - 4.another_image.gif
   |
   - 2
       |
       - 1.icon.gif
       - 2.another_icon.gif

General usage

  attachment = Attachment.new("images/my_image.gif", :node_id => 1)

This will create an instance of an attachment that belongs to node with ID = 0 Nothing has bee saved yet

  attachment.save

This will save the attachment in the attachment directory structure

You can inspect the saved instance:

  attachment.node_id
  attachment.id
  attachment.filename
  attachment.fullpath

  attachments = Attachment.find(:all)

Creates an array instance that contains all the attachments

  Attachment.find(:all, :conditions => {:node_id => 1})

Creates an array instance that contains all the attachments for node with ID=1

  Attachment.find('test.gif', :conditions => {:node_id => 1})

Retrieves the test.gif image that is associated with node 1

Methods
Constants
AttachmentPwd = ENV["RAILS_ENV"] == "test" ? File.join(RAILS_ROOT, 'tmp', 'attachments') : File.join( RAILS_ROOT, 'attachments')
  Set the path to the attachment storage
Attributes
[RW] filename
[R] id
[RW] node_id
[RW] tempfile
Public Class methods
find(*args)

Return the attachment instance(s) based on the find parameters

     # File app/models/attachment.rb, line 124
124:   def self.find(*args)
125:     options = args.extract_options!
126:     dir = Dir.new(AttachmentPwd)
127: 
128:     # makes the find request and stores it to resources
129:     return_value = case args.first
130:     when :all, :first, :last
131:       attachments = []
132:       if options[:conditions] && options[:conditions][:node_id]
133:         node_id = options[:conditions][:node_id].to_s
134:         raise "Node with ID=#{node_id} does not exist" unless Node.exists?(node_id)
135:         if (File.exist?( File.join(AttachmentPwd, node_id)))
136:           node_dir = Dir.new( File.join(AttachmentPwd, node_id) )
137:           node_dir.each do |attachment|
138:             next unless (attachment =~ /^(.+)$/) == 0 && !File.directory?(attachment)
139:             attachments << Attachment.new(:filename => $1, :node_id => node_id.to_i)
140:           end
141:         end
142:       else
143:         dir.each do |node|
144:           next unless node =~ /^\d*$/
145:           node_dir = Dir.new( File.join(AttachmentPwd, node) )
146:           node_dir.each do |attachment|
147:             next unless (attachment =~ /^(.+)$/) == 0 && !File.directory?(attachment)
148:             attachments << Attachment.new(:filename => $1, :node_id => node.to_i)
149:           end
150:         end
151:         attachments.sort! {|a,b| a.filename <=> b.filename }
152:       end
153: 
154:       # return based on the request arguments
155:       case args.first
156:       when :first
157:         attachments.first
158:       when :last
159:         attachments.last
160:       else
161:         attachments
162:       end
163:     else
164:       # in this routine we find the attachment by file name and node id
165:       filename = args.first
166:       attachments = []
167:       raise "You need to supply a node id in the condition parameter" unless options[:conditions] && options[:conditions][:node_id]
168:       node_id = options[:conditions][:node_id].to_s
169:       raise "Node with ID=#{node_id} does not exist" unless Node.exists?(node_id)
170:       node_dir = Dir.new( File.join(AttachmentPwd, node_id) )
171:       node_dir.each do |attachment|
172:         next unless ((attachment =~ /^(.+)$/) == 0 && $1 == filename)
173:         attachments << Attachment.new(:filename => $1, :node_id => node_id.to_i)
174:       end
175:       raise "Could not find Attachment with filename #{filename}" if attachments.empty?
176:       attachments.first
177:     end
178:     return return_value
179:   end
new(*args)

Initializes the attachment instance

    # File app/models/attachment.rb, line 74
74:   def initialize(*args)
75:     options = args.extract_options!
76:     @filename = options[:filename]
77:     @node_id = options[:node_id]
78:     #@id = options[:id]
79:     @tempfile = args[0] || options[:tempfile]
80: 
81:     if File.exists?(fullpath) && File.file?(fullpath)
82:       super(fullpath, 'rb+')
83:       @initialfile = fullpath.clone
84:     elsif @tempfile && File.exists?(@tempfile)
85:       super(@tempfile, 'rb+')
86:     elsif @tempfile && File.basename(@tempfile) != ''
87:       @initialfile = File.join( RAILS_ROOT, 'tmp', File.basename(@tempfile))
88:       super(@initialfile, 'wb+')
89:     else
90:       raise "No physical file available"
91:     end
92: 
93:   end
pwd()

Class method that returns the path to the attachment storage

     # File app/models/attachment.rb, line 182
182:   def self.pwd
183:     AttachmentPwd
184:   end
Public Instance methods
delete()

Deletes the file that the instance is pointing to from memory

     # File app/models/attachment.rb, line 115
115:   def delete
116:     self.close
117:     if ( !@initialfile || (File.dirname(@initialfile) == File.join(RAILS_ROOT, 'tmp')) )
118:       raise "No physical file to delete"
119:     end
120:     FileUtils.rm(@initialfile)
121:   end
fullpath()

Retruns the full path of an attachment on the file system

     # File app/models/attachment.rb, line 187
187:   def fullpath
188:     File.join(AttachmentPwd, @node_id.to_s, @filename.to_s)
189:   end
save()

Closes the current file handle, this writes the content to the file system

     # File app/models/attachment.rb, line 96
 96:   def save
 97:     if File.exists?(fullpath) && File.file?(fullpath)
 98:       self.close
 99:     else
100:       raise "Node with ID=#{@node_id} does not exist" unless @node_id && Node.exists?(@node_id)
101:       
102:       @filename ||= File.basename(@tempfile)
103:       FileUtils.mkdir(File.dirname(fullpath)) unless File.exists?(File.dirname(fullpath))
104:       self.close
105:       FileUtils.cp(self.path, fullpath) if @intialfile != fullpath
106:       if ( @initialfile && @initialfile != fullpath )
107:         # If we are still a temp file
108:         FileUtils.rm(@initialfile)
109:       end
110:       @initialfile = fullpath.clone
111:     end
112:   end
to_json(options={})

Provide a JSON representation of this object that can be understood by components of the web interface

     # File app/models/attachment.rb, line 193
193:   def to_json(options={})
194:     {
195:       :filename => @filename,
196:       :size => File.size(self.fullpath),
197:       :created_at => self.ctime
198:     }.to_json(options)
199:   end