cartoonist 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,11 +9,17 @@ class AdminController < CartoonistController
9
9
  def backup
10
10
  respond_to do |format|
11
11
  format.html { redirect_to "/admin/main" }
12
- format.json do
13
- prefix = "dev-" unless Rails.env.production?
14
- filename = "#{prefix}comics-backup-#{Time.now.strftime("%Y-%m-%d_%H%M%S")}.json"
15
- headers["Content-Disposition"] = "attachment; filename=\"#{filename}\""
16
- render :text => Backup.all.to_json, :content_type => "application/json"
12
+
13
+ format.tgz do
14
+ backup = Backup.new :tgz
15
+ headers["Content-Disposition"] = backup.content_disposition
16
+ self.response_body = backup.response_body
17
+ end
18
+
19
+ format.zip do
20
+ backup = Backup.new :zip
21
+ headers["Content-Disposition"] = backup.content_disposition
22
+ self.response_body = backup.response_body
17
23
  end
18
24
  end
19
25
  end
@@ -46,6 +46,21 @@ class CartoonistController < ActionController::Base
46
46
  end
47
47
 
48
48
  def cache_page_as(path)
49
+ if block_given?
50
+ expiration = expiration_for path
51
+ response.headers["Expires"] = CGI.rfc1123_date expiration.from_now
52
+ expires_in expiration, :public => true
53
+ yield
54
+ end
55
+
49
56
  cache_page @response, "/cache/#{path}"
50
57
  end
58
+
59
+ def expiration_for(path)
60
+ if path =~ /\.tmp\.[^.]*$/
61
+ 2.hours
62
+ else
63
+ 7.days
64
+ end
65
+ end
51
66
  end
@@ -5,8 +5,10 @@ class SiteController < CartoonistController
5
5
 
6
6
  format.ico do
7
7
  data = ActionController::Base.helpers.asset_paths.asset_environment[Cartoonist::Theme.favicon].to_s
8
- send_data data, :filename => "favicon.ico", :type => "image/x-icon", :disposition => "inline"
9
- cache_page_as "static/favicon.ico"
8
+
9
+ cache_page_as "static/favicon.ico" do
10
+ send_data data, :filename => "favicon.ico", :type => "image/x-icon", :disposition => "inline"
11
+ end
10
12
  end
11
13
  end
12
14
  end
@@ -16,8 +18,9 @@ class SiteController < CartoonistController
16
18
  format.html { redirect_to "/" }
17
19
 
18
20
  format.text do
19
- render :layout => false
20
- cache_page_as "static/robots.txt"
21
+ cache_page_as "static/robots.txt" do
22
+ render :layout => false
23
+ end
21
24
  end
22
25
  end
23
26
  end
data/app/models/backup.rb CHANGED
@@ -1,11 +1,110 @@
1
1
  class Backup
2
+ ALLOWED_EXTENSIONS = [:tgz, :zip]
3
+
4
+ def initialize(extension)
5
+ raise "Invalid extension '#{extension}'" unless ALLOWED_EXTENSIONS.include? extension.to_sym
6
+ @extension = extension.to_sym
7
+ end
8
+
9
+ def content_disposition
10
+ %{attachment; filename="#{filename}"}
11
+ end
12
+
13
+ def filename
14
+ prefix = "dev-" unless Rails.env.production?
15
+ "#{prefix}cartoonist-backup-#{Time.now.strftime("%Y-%m-%d_%H%M%S")}.#{@extension}"
16
+ end
17
+
18
+ def response_body
19
+ Enumerator.new do |out|
20
+ send @extension, out
21
+ end
22
+ end
23
+
24
+ def zip(out)
25
+ buffer = Zip::ZipOutputStream.write_buffer do |zos|
26
+ Backup.each do |entry|
27
+ zos.put_next_entry entry.path
28
+ zos.write entry.content
29
+ end
30
+ end
31
+
32
+ out << buffer.string
33
+ end
34
+
35
+ def tgz(out)
36
+ gzip = Zlib::GzipWriter.new Backup::ResponseOutWriter.new(out)
37
+
38
+ begin
39
+ Archive::Tar::Minitar::Writer.open gzip do |tar|
40
+ Backup.each do |entry|
41
+ tar.add_file_simple entry.path, :mode => 0644, :size => entry.content.length do |output|
42
+ output.write entry.content
43
+ end
44
+ end
45
+ end
46
+ ensure
47
+ gzip.close
48
+ end
49
+ end
50
+
51
+ class ResponseOutWriter < Struct.new(:stream)
52
+ def write(content)
53
+ stream << content
54
+ end
55
+ end
56
+
2
57
  class << self
3
- def all
4
- result = Cartoonist::Backup.all.map do |key, value|
5
- [key, value.call]
58
+ def each
59
+ Cartoonist::Backup.all.each do |key, proc|
60
+ proc.call.find_each do |value|
61
+ if value.respond_to? :to_backup_entries
62
+ value.to_backup_entries.each do |entry|
63
+ yield entry.with_key(key)
64
+ end
65
+ else
66
+ yield Backup::Entry.from_record(value).with_key(key)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ class Entry
74
+ attr_reader :key, :filename, :content
75
+
76
+ def initialize(id, title, extension, content)
77
+ if title
78
+ title = "%05d_%s" % [id, title]
79
+ else
80
+ title = "%05d" % id
6
81
  end
7
82
 
8
- Hash[result]
83
+ title = DatabaseFile.sanitize title
84
+ extension = DatabaseFile.sanitize(extension || "")
85
+ extension = ".#{extension}" if extension.present?
86
+ @filename = "#{title}#{extension}"
87
+ @content = content
88
+ end
89
+
90
+ def with_key(key)
91
+ @key = key
92
+ self
93
+ end
94
+
95
+ def safe_key
96
+ DatabaseFile.sanitize key.to_s
97
+ end
98
+
99
+ def path
100
+ "#{safe_key}/#{filename}"
101
+ end
102
+
103
+ class << self
104
+ def from_record(record)
105
+ title = record.zip_title if record.respond_to? :zip_title
106
+ new record.id, title, "json", record.to_json
107
+ end
9
108
  end
10
109
  end
11
110
  end
@@ -1,3 +1,36 @@
1
1
  class DatabaseFile < ActiveRecord::Base
2
- attr_accessible :filename, :content
2
+ attr_accessible :filename, :extension, :content
3
+
4
+ def to_backup_entries
5
+ if filename
6
+ meta_name = "#{filename}.meta"
7
+ else
8
+ meta_name = "meta"
9
+ end
10
+
11
+ file = Backup::Entry.new id, filename, extension, content
12
+ meta = Backup::Entry.new id, meta_name, "json", to_json(:except => :content)
13
+ [file, meta]
14
+ end
15
+
16
+ class << self
17
+ def sanitize(value)
18
+ value.gsub(/\s+/, "_").gsub(/[^0-9a-zA-Z.\-_]/, "")
19
+ end
20
+
21
+ def create_from_param(file, options = {})
22
+ original_filename = file.original_filename
23
+ extension = File.extname original_filename
24
+ filename = File.basename original_filename, extension
25
+ filename = nil if filename.blank?
26
+ extension = extension[/^\.?(.*?)$/, 1].downcase if extension
27
+ extension = nil if extension.blank?
28
+
29
+ if options.include?(:allowed_extensions) && !options[:allowed_extensions].map(&:downcase).include?(extension)
30
+ raise "Extension must be one of: #{options[:allowed_extensions].join ", "}"
31
+ end
32
+
33
+ create :filename => filename, :extension => extension, :content => file.read
34
+ end
35
+ end
3
36
  end
@@ -1,13 +1,33 @@
1
1
  class Markdown
2
+ class LinkToAbsoluteRenderer < Redcarpet::Render::HTML
3
+ def link(link, title, content)
4
+ link = "http://#{Setting[:domain]}#{link}" if link =~ /^\//
5
+ title = %{ title="#{title}"} if title
6
+ %{<a href="#{link}"#{title}>#{content}</a>}
7
+ end
8
+ end
9
+
2
10
  class << self
3
- def config
4
- @markdown ||= Redcarpet::Markdown.new Redcarpet::Render::HTML
11
+ RENDER_DEFAULTS = { :html_safe => true, :link_to_absolute => false }
12
+
13
+ def standard_renderer
14
+ @standard_renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML)
5
15
  end
6
16
 
7
- def render(text, safe = true)
8
- result = config.render text
17
+ def link_to_absolute_renderer
18
+ @link_to_absolute_renderer ||= Redcarpet::Markdown.new(LinkToAbsoluteRenderer.new)
19
+ end
20
+
21
+ def render(text, options = {})
22
+ options = RENDER_DEFAULTS.merge options
23
+
24
+ if options[:link_to_absolute]
25
+ result = link_to_absolute_renderer.render text
26
+ else
27
+ result = standard_renderer.render text
28
+ end
9
29
 
10
- if safe
30
+ if options[:html_safe]
11
31
  result.html_safe
12
32
  else
13
33
  result
@@ -1,6 +1,10 @@
1
1
  class Setting < ActiveRecord::Base
2
2
  attr_accessible :label, :value, :locked
3
3
 
4
+ def zip_title
5
+ label
6
+ end
7
+
4
8
  class << self
5
9
  def [](label)
6
10
  raise "Invalid label" unless label.present?
data/app/models/user.rb CHANGED
@@ -4,6 +4,10 @@ class User < ActiveRecord::Base
4
4
  # Setup accessible (or protected) attributes for your model
5
5
  attr_accessible :email, :password, :password_confirmation, :remember_me, :name
6
6
 
7
+ def zip_title
8
+ name
9
+ end
10
+
7
11
  class << self
8
12
  def create_user(params)
9
13
  create :email => params[:email], :name => params[:name], :password => params[:password], :password_confirmation => params[:confirm_password]
@@ -3,5 +3,9 @@
3
3
  </p>
4
4
 
5
5
  <p>
6
- <a href="/admin/backup.json"><%= t "admin.general.actions.backup" %></a>
6
+ <a href="/admin/backup.tgz"><%= t "admin.general.actions.backup_tgz" %></a>
7
+ </p>
8
+
9
+ <p>
10
+ <a href="/admin/backup.zip"><%= t "admin.general.actions.backup_zip" %></a>
7
11
  </p>
data/cartoonist.gemspec CHANGED
@@ -14,4 +14,6 @@ Gem::Specification.new do |s|
14
14
  s.add_dependency "jquery-rails", "~> 2.0.0"
15
15
  s.add_dependency "railties", "~> 3.2.0"
16
16
  s.add_dependency "redcarpet", "~> 2.1.0"
17
+ s.add_dependency "rubyzip", "~> 0.9.0"
18
+ s.add_dependency "minitar", "~> 0.5.0"
17
19
  end
@@ -34,7 +34,8 @@ en:
34
34
  users: Users
35
35
  general:
36
36
  actions:
37
- backup: Download Backup
37
+ backup_tgz: Download Backup (tarball)
38
+ backup_zip: Download Backup (zip)
38
39
  reload: Update and Reload
39
40
  layout:
40
41
  actions: Actions
@@ -0,0 +1,6 @@
1
+ class AddExtensionToDatabaseFiles < ActiveRecord::Migration
2
+ def change
3
+ add_column :database_files, :extension, :string
4
+ DatabaseFile.update_all :extension => "png"
5
+ end
6
+ end
@@ -294,19 +294,20 @@ module Cartoonist
294
294
  end
295
295
 
296
296
  Mime::Type.register "image/x-icon", :ico
297
+ Mime::Type.register "application/octet-stream", :tgz
297
298
  Cartoonist::Admin::Tab.add :general, :url => "/admin", :order => 3
298
299
  Cartoonist::Migration.add_for self
299
300
 
300
301
  Cartoonist::Backup.for :files do
301
- DatabaseFile.order(:id).all
302
+ DatabaseFile.order(:id)
302
303
  end
303
304
 
304
305
  Cartoonist::Backup.for :settings do
305
- Setting.order(:id).all
306
+ Setting.order(:id)
306
307
  end
307
308
 
308
309
  Cartoonist::Backup.for :users do
309
- User.order(:id).all
310
+ User.order(:id)
310
311
  end
311
312
 
312
313
  Cartoonist::Cron.add do
data/lib/cartoonist.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require "devise"
2
2
  require "jquery-rails"
3
3
  require "redcarpet"
4
+ require "zlib"
5
+ require "archive/tar/minitar"
6
+ require "zip/zip"
4
7
 
5
8
  module Cartoonist
6
9
  module Admin
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cartoonist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-15 00:00:00.000000000 Z
12
+ date: 2012-05-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: devise
16
- requirement: &11950700 !ruby/object:Gem::Requirement
16
+ requirement: &12204220 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 2.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *11950700
24
+ version_requirements: *12204220
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: jquery-rails
27
- requirement: &11937860 !ruby/object:Gem::Requirement
27
+ requirement: &12203580 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 2.0.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *11937860
35
+ version_requirements: *12203580
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: railties
38
- requirement: &11935680 !ruby/object:Gem::Requirement
38
+ requirement: &12203040 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 3.2.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *11935680
46
+ version_requirements: *12203040
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: redcarpet
49
- requirement: &11934640 !ruby/object:Gem::Requirement
49
+ requirement: &12273440 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,29 @@ dependencies:
54
54
  version: 2.1.0
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *11934640
57
+ version_requirements: *12273440
58
+ - !ruby/object:Gem::Dependency
59
+ name: rubyzip
60
+ requirement: &12272860 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 0.9.0
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *12272860
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitar
71
+ requirement: &12272340 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 0.5.0
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *12272340
58
80
  description: This provides the main functionality and plugin api for Cartoonist.
59
81
  email: reasonnumber@gmail.com
60
82
  executables: []
@@ -110,6 +132,7 @@ files:
110
132
  - db/migrate/20120320043253_create_database_files.rb
111
133
  - db/migrate/20120401014029_create_settings.rb
112
134
  - db/migrate/20120417075756_devise_create_users.rb
135
+ - db/migrate/20120524032959_add_extension_to_database_files.rb
113
136
  - lib/cartoonist.rb
114
137
  - lib/cartoonist/engine.rb
115
138
  - public/errors/404.html