parcel 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ require File.join(File.dirname(__FILE__), "parcel", "has_parcel")
2
+ require File.join(File.dirname(__FILE__), "parcel", "zip_file_repository")
3
+
4
+ module Parcel
5
+
6
+ @options = Hash.new
7
+
8
+ def self.storage_root
9
+ @options[:storage_root] || "./:class_name/:id"
10
+ end
11
+
12
+ def self.storage_root=(value)
13
+ @options[:storage_root] = value
14
+ end
15
+
16
+ def self.temp_path(filename)
17
+ date_stamp = "%04d_%02d_%02d" % [Time.now.year, Time.now.month, Time.now.day]
18
+ path = File.join(temp_root, date_stamp, "#{Time.now.to_i}_#{rand(999999)}_#{filename}")
19
+ FileUtils.mkdir_p( File.dirname(path) )
20
+ path
21
+ end
22
+
23
+ def self.temp_root=(value)
24
+ @options[:temp_root] = value
25
+ end
26
+
27
+ def self.temp_root
28
+ @options[:temp_root] || "/tmp/parcel"
29
+ end
30
+
31
+ def self.underscore(camel_cased_word)
32
+ camel_cased_word.to_s.gsub(/::/, '/').
33
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
34
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
35
+ tr("-", "_").
36
+ downcase
37
+ end
38
+
39
+ def self.interpolate_path(path, options, reference)
40
+ new_path = path.to_s
41
+
42
+ framework_opts = Hash.new
43
+ if defined?(Rails)
44
+ framework_opts[:rails_root] = Rails.root
45
+ framework_opts[:id] = reference.id if reference.is_a?(ActiveRecord::Base)
46
+ end
47
+
48
+ framework_opts[:id] ||= reference.object_id
49
+
50
+ merge_opts = framework_opts.merge({ :class_name => underscore(reference.class.name) }).merge(options)
51
+
52
+ puts merge_opts.inspect
53
+
54
+ merge_opts.to_a.sort_by { |(k,v)| -k.to_s.length }.each do |(k,v)|
55
+ new_path = new_path.gsub(":#{k}", v.to_s)
56
+ end
57
+
58
+ new_path
59
+ end
60
+
61
+ def self.create_repository(owner, options, source = nil)
62
+ repo = if options[:class] == :zip
63
+ ZipFileRepository.new(owner, options)
64
+ else
65
+ nil
66
+ end
67
+
68
+ raise ArgumentError, "Repository not supported: #{options[:class]}" if repo.nil?
69
+
70
+ if source.is_a?(String) && source.starts_with?("/")
71
+ repo.import(File.open(source, "r"))
72
+ elsif source.is_a?(String)
73
+ repo.import_data(source)
74
+ elsif source.respond_to?(:to_file)
75
+ repo.import(source.to_file)
76
+ elsif source.respond_to?(:read)
77
+ repo.import(source)
78
+ elsif File.exist?(repo.commit_path)
79
+ repo.import(File.open(repo.commit_path, "r"))
80
+ end
81
+
82
+ repo
83
+ end
84
+
85
+ end
86
+
87
+ Object.send(:include, Parcel::HasParcel)
@@ -0,0 +1,63 @@
1
+ module Parcel
2
+ module HasParcel
3
+ def self.included(base)
4
+ base.module_eval do
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+ end
8
+ end
9
+
10
+ module InstanceMethods
11
+ def parcel_data_path(filename)
12
+ data_path = File.join(Parcel.interpolate_path(Parcel.storage_root, Hash.new, self), filename.to_s)
13
+ data_path
14
+ end
15
+
16
+ def commit_parcels!
17
+ Array(instance_variables.select { |v| v =~ /\@_parcel_/ }).each do |name|
18
+ instance = instance_variable_get(name)
19
+ instance.commit! if instance
20
+ end
21
+
22
+ true
23
+ end
24
+
25
+ def destroy_parcels!
26
+ FileUtils.rm_rf(self.class.parcel_data_path("."))
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+
32
+ def has_parcel(name, options = {})
33
+ options = { :class => :zip }.merge(options).merge({ :name => name })
34
+
35
+ unless included_modules.include?(InstanceMethods)
36
+ include(InstanceMethods)
37
+
38
+ if defined?(ActiveRecord) && is_a?(ActiveRecord::Base)
39
+ after_save :commit_parcels!
40
+ after_destroy :destroy_parcels!
41
+ end
42
+ end
43
+
44
+ define_method(name.to_sym) do
45
+ instance_variable_get("@_parcel_#{name}") || begin
46
+ parcel = Parcel.create_repository(self, options)
47
+ instance_variable_set("@_parcel_#{name}", parcel)
48
+ parcel
49
+ end
50
+ end
51
+
52
+ define_method("#{name}=".to_sym) do |input|
53
+ instance = instance_variable_get("@_parcel_#{name}")
54
+ instance.destroy if instance
55
+
56
+ instance_variable_set("@_parcel_#{name}", Parcel.create_repository(self, options, input))
57
+ end
58
+
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,97 @@
1
+ require 'fileutils'
2
+ require 'zip/zip'
3
+ require 'ostruct'
4
+
5
+ module Parcel
6
+
7
+ # The FileRepository class represents a compressed archive of files
8
+ # providing an easy interface for enumerating and updating the contents.
9
+ class ZipFileRepository
10
+
11
+ # Create a blank FileRepository, or open one from an existing file object.
12
+ def initialize(owner = nil, options = Hash.new)
13
+ @temp_file = Parcel.temp_path("#{owner.object_id}_#{rand(99999)}.zip")
14
+ @owner = owner
15
+ @name = options[:name]
16
+ end
17
+
18
+ def import(source)
19
+ File.open(@temp_file, "w") { |f| f.write source.read }
20
+ end
21
+
22
+ def import_data(data)
23
+ File.open(@temp_file, "w") { |f| f.write data }
24
+ end
25
+
26
+ def commit_path
27
+ @owner.parcel_data_path(@name) + ".zip"
28
+ end
29
+
30
+ # Returns true if there are no files in the repository, or if the attachment is nil.
31
+ def blank?
32
+ files.blank?
33
+ end
34
+
35
+ # Clears the repository.
36
+ def clear!
37
+ File.unlink(@temp_file) if File.exist?(@temp_file)
38
+
39
+ Zip::ZipFile.open( @temp_file, Zip::ZipFile::CREATE ) do
40
+ end
41
+ end
42
+
43
+ # Returns all the files present in the repository.
44
+ def files
45
+ return [] if @temp_file.nil? || !File.exist?(@temp_file)
46
+
47
+ result = Array.new
48
+ Zip::ZipFile.foreach(@temp_file) { |entry| result << OpenStruct.new(:name => entry.name, :size => entry.size) }
49
+ result
50
+ end
51
+
52
+ # Returns the contents of the given file. Supports wildcard matching,
53
+ # in which case returns the contents of the first file found.
54
+ def read_file(filename)
55
+ return nil if blank?
56
+
57
+ file_match = Regexp.new("^" + filename.gsub(".", "\\.").gsub("*", ".*").gsub("?", ".") + "$", Regexp::IGNORECASE)
58
+
59
+ Zip::ZipFile.foreach(@temp_file) do |entry|
60
+ if entry.name =~ file_match
61
+ read_file = Parcel.temp_path(File.basename(entry.name))
62
+ entry.extract(read_file)
63
+ result = IO.read(read_file)
64
+ File.unlink(read_file)
65
+ return result
66
+ end
67
+ end
68
+
69
+ return nil
70
+ end
71
+
72
+ # Adds a file to the repository.
73
+ def add_file(filename, contents)
74
+ Zip::ZipFile.open( @temp_file, Zip::ZipFile::CREATE ) do |writer|
75
+ writer.get_output_stream(filename) { |file| file.write contents }
76
+ end
77
+ end
78
+
79
+ # Returns an open file stream of the repository.
80
+ def to_file
81
+ blank? ? nil : File.open(@temp_file)
82
+ end
83
+
84
+ def save_to(destination)
85
+ FileUtils.mkdir_p(File.dirname(destination))
86
+ FileUtils.cp(@temp_file, destination) if @temp_file && File.exist?(@temp_file)
87
+ end
88
+
89
+ def commit!
90
+ raise ArgumentError, "No owner defined, use \#save_to instead" if @owner == nil
91
+ save_to(commit_path)
92
+ end
93
+
94
+ end
95
+
96
+
97
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parcel
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Sean St. Quentin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-15 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubyzip
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: " Parcel is a simple library which allows normal ruby objects to maintain a series\n of files stored in one or more repositories which are accessible through normal ruby attributes.\n Possible repositories include Zip Files, Tar Files, Subversion Repos, Repos, GridFS Zips, etc.\n It also provides an common and easy to use interface to all these repositories (i.e. more ruby like than RubyZip), and\n works with ActiveRecord but doesn't depend on it.\n"
26
+ email:
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/parcel.rb
35
+ - lib/parcel/has_parcel.rb
36
+ - lib/parcel/zip_file_repository.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/elseano/parcel
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Simple repository management
65
+ test_files: []
66
+