smallcage 0.1.9 → 0.2.0

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.
data/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.2.0 2009-11-13
2
+
3
+ * add Google Data project template.
4
+ * fix some problem at cache control project template.
5
+ * add cache clear tasks.
6
+ * update gem summary and description.
7
+
1
8
  == 0.1.9 2009-11-09
2
9
 
3
10
  * add uri command.
data/README.rdoc CHANGED
@@ -1,5 +1,16 @@
1
1
  = SmallCage -- simple website generator
2
2
 
3
+ SmallCage is a simple, but powerful website generator. It converts content and template files, which has common elements in a website, to a plain, static website. No database, no application container, and no repeat in many pages is needed. You can keep your site well with very little work.
4
+
5
+ With SmallCage, you can
6
+
7
+ * simply separate your template from content.
8
+ * convert your content to a plain, static website. It's easy to serve and test.
9
+ * generate multi pages from a single CSV file or even Google Docs spreadsheet.
10
+ * share your template with various web applications: Ruby on Rails, WordPress, and MovableType.
11
+ * manage your website with a source code management system like a Git, Subversion, and CVS.
12
+ * customize the rules of convert with Ruby.
13
+
3
14
 
4
15
  == Installation
5
16
 
@@ -17,11 +28,12 @@ If successfully installed, smc command will be available.
17
28
  update [path] Build smc contents.
18
29
  clean [path] Remove files generated from *.smc source.
19
30
  server [path] [port] Start HTTP server.
20
- auto [path] [port] Start auto update daemon.
31
+ auto [path] [port] Start auto update server.
21
32
  import [name|uri] Import project.
22
33
  export [path] [outputpath] Export project.
34
+ uri [path] Print URIs.
23
35
  manifest [path] Generate Manifest.html file.
24
-
36
+
25
37
  Options are:
26
38
 
27
39
  -h, --help Show this help message.
data/Rakefile CHANGED
@@ -5,8 +5,8 @@ begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "smallcage"
8
- gem.summary = "Lightweight CMS package."
9
- gem.description = "Lightweight CMS package."
8
+ gem.summary = "simple website generator"
9
+ gem.description = "SmallCage is a simple, but powerful website generator. It converts content and template files, which has common elements in a website, to a plain, static website. No database, no application container, and no repeat in many pages is needed. You can keep your site well with very little work."
10
10
  gem.email = "smallcage@googlegroups.com"
11
11
  gem.homepage = "http://www.smallcage.org"
12
12
  gem.authors = ["SAITO Toshihiro", "gommmmmm", "KOSEKI Kengo"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.9
1
+ 0.2.0
@@ -1,8 +1,8 @@
1
1
  module SmallCage #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 1
5
- TINY = 9
4
+ MINOR = 2
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,11 +1,16 @@
1
+ require "rexml/document"
2
+
1
3
  module SmallCage
2
4
  class CacheFilter
5
+ TARGET_EXT = %w{css js png gif jpg ico}
6
+ TARGET_PATTERN = "**/*--latest.{#{TARGET_EXT.join(",")}}"
7
+ TARGET_SRC_REX = %r{(["'])(?!https?://)([^"']+--latest\.(?:#{TARGET_EXT.join("|")}))(["'])}
3
8
 
4
9
  def initialize(opts)
5
10
  end
6
11
 
7
12
  def after_rendering_filter(obj, str)
8
- str.gsub %r{(\s(?:src|href)=["'])(?!https?://)([^"']+--latest\.(?:css|js|png|gif|jpg))(["'])} do
13
+ str.gsub TARGET_SRC_REX do
9
14
  pre = $1
10
15
  path = $2
11
16
  pro = $3
@@ -20,10 +25,21 @@ module SmallCage
20
25
 
21
26
  rex = /^(.+)--latest(\.[^.]+)$/
22
27
  pattern = relpath.to_s.sub(rex, '\1-*\2')
23
-
28
+ pre = $1
29
+ pro = $2
30
+
31
+ # Search largest numbered file.
24
32
  entry = nil
25
33
  Dir.chdir(dir) do
26
- entry = Dir.glob(pattern).reject {|f| f == relpath }.sort.last
34
+ files = []
35
+ Dir.glob(pattern).each do |f|
36
+ next if f == relpath
37
+ number = f[pre.length ... -pro.length]
38
+ next unless number =~ /^-0*(\d+)$/
39
+ files << [f, $1.to_i]
40
+ end
41
+ files.reject {|f| f == relpath }
42
+ entry = files.sort{|a,b| a[1] <=> b[1] }.last.to_a[0]
27
43
  end
28
44
  return path unless entry
29
45
 
@@ -31,5 +47,53 @@ module SmallCage
31
47
  return entry
32
48
  end
33
49
  private :find_latest
50
+
51
+
52
+ # Get svn revision of file path + ".smc" or path
53
+ def self.get_revision(path)
54
+ smcpath = Pathname.new(path.to_s + ".smc")
55
+ path = smcpath if smcpath.file?
56
+
57
+ src = %x{svn info --xml #{path}}
58
+ begin
59
+ doc = REXML::Document.new(src)
60
+ revision = doc.elements['/info/entry/commit/@revision'].value
61
+ return revision
62
+ rescue
63
+ puts "Can't get revision number: #{path}"
64
+ return "0"
65
+ end
66
+ end
67
+
68
+ def self.outfiles(srcfile, outfiles)
69
+ r = srcfile.rindex("--latest")
70
+ prefix = srcfile[0..r]
71
+ suffix = srcfile[r + 8 .. -1]
72
+ result = []
73
+ outfiles.each do |f|
74
+ if f[0..r] == prefix && f[- suffix.length .. -1] == suffix
75
+ rev = f[r ... -suffix.length]
76
+ if rev =~ /^-(\d+)$/
77
+ result << [f, $1.to_i]
78
+ end
79
+ end
80
+ end
81
+ return result.sort {|a,b| a[1] <=> b[1]}
82
+ end
83
+
84
+ def self.create_cache(list, dryrun, quiet = false)
85
+ list.each do |path|
86
+ revision = SmallCage::CacheFilter.get_revision(path)
87
+ to = path.pathmap("%{--latest$,-#{revision}}X%x")
88
+ puts File.exist?(to) ? "(cache)U #{to}" : "(cache)A #{to}" unless quiet
89
+ begin
90
+ FileUtils.copy(path,to) unless dryrun
91
+ rescue => e
92
+ puts " ERROR: #{e} #{path} -> #{to}"
93
+ end
94
+ end
95
+ end
96
+
34
97
  end
98
+
35
99
  end
@@ -1,34 +1,56 @@
1
-
2
1
  namespace :cache do
3
-
4
- task :update do
5
- require "rexml/document"
6
- list = FileList["**/*--latest.{css,js,png,gif,jpg}"]
7
-
2
+
3
+ desc "Delete all cache files."
4
+ task :clean => :require do
5
+ pat = SmallCage::CacheFilter::TARGET_PATTERN
6
+ list = FileList[pat]
8
7
  list.each do |path|
9
- src = %x{svn info --xml #{path}}
10
- begin
11
- doc = REXML::Document.new(src)
12
- revision = doc.elements['/info/entry/commit/@revision'].value
13
- rescue
14
- puts "Can't get revision number: #{path}"
8
+ to = path.pathmap("%{--latest$,-*}X%x")
9
+ outfiles = FileList[to]
10
+ outfiles = SmallCage::CacheFilter.outfiles(path, outfiles)
11
+ outfiles.each do |f|
12
+ puts "(cache)D #{f[0]}"
13
+ File.delete(f[0])
15
14
  end
16
- to = path.pathmap("%{--latest$,-#{revision}}X%x")
17
- if File.exist?(to)
18
- puts " SKIP(exists): #{path} -> #{to}"
19
- next
20
- else
21
- puts " COPY: #{path} -> #{to}"
22
- end
23
- unless ENV["DRYRUN"]
24
- next if File.exist?(to)
25
- begin
26
- FileUtils.copy(path,to)
27
- rescue => e
28
- puts " ERROR: #{e} #{path} -> #{to}"
29
- end
15
+ end
16
+ end
17
+
18
+ desc "Delete old cache files."
19
+ task :delete_old => :require do
20
+ pat = SmallCage::CacheFilter::TARGET_PATTERN
21
+ list = FileList[pat]
22
+ list.each do |path|
23
+ to = path.pathmap("%{--latest$,-*}X%x")
24
+ outfiles = FileList[to]
25
+ outfiles = SmallCage::CacheFilter.outfiles(path, outfiles)
26
+ outfiles.pop
27
+ outfiles.each do |f|
28
+ puts "(cache)D #{f[0]}"
29
+ File.delete(f[0])
30
30
  end
31
31
  end
32
32
  end
33
+
34
+ task :require do
35
+ require File.dirname(__FILE__) + "/../filters/cache_filter.rb"
36
+ end
37
+
38
+ desc "Update *--latest.* files."
39
+ task :update => [:require] do
40
+ pat = SmallCage::CacheFilter::TARGET_PATTERN
41
+
42
+ # Fix filenames. (site--latest.css.smc -> site--latest.css -> site-123.css)
43
+ smclist = FileList["#{pat}.smc"]
44
+ system "smc update" unless smclist.empty?
45
+ list = FileList[pat]
46
+ SmallCage::CacheFilter.create_cache(list, ENV["DRYRUN"])
47
+
48
+ # Apply cache filter. Rewrite links. (site--latest.css.smc -> site--latest.css)
49
+ system "smc update"
50
+
51
+ # Copy updated file (site--latest.css -> site-123.css)
52
+ smclist = smclist.map {|f| f[0 .. -5]}
53
+ SmallCage::CacheFilter.create_cache(smclist, ENV["DRYRUN"])
54
+ end
33
55
 
34
56
  end
@@ -0,0 +1,227 @@
1
+ require "rubygems"
2
+ require "gdata"
3
+ require "yaml"
4
+ require "highline/import"
5
+
6
+ class GDataExporter
7
+ CONFIG_FILE = Pathname.new(File.dirname(__FILE__)).realpath + "../../_dir.smc"
8
+ AUTH_ROOT = Pathname.new(File.expand_path("~/.smallcage"))
9
+
10
+ def initialize
11
+ load_config
12
+ end
13
+
14
+ def load_config
15
+ if File.file?(CONFIG_FILE)
16
+ @config = YAML.load_file(CONFIG_FILE)
17
+ else
18
+ @config ||= {}
19
+ end
20
+ @config["gdata_auth"] ||= "default"
21
+ end
22
+ private :load_config
23
+
24
+ def auth_file
25
+ return AUTH_ROOT + "gdata_auth_#{@config["gdata_auth"]}.yml"
26
+ end
27
+ private :auth_file
28
+
29
+ def umask_close
30
+ old = File.umask
31
+ File.umask(077)
32
+ begin
33
+ yield
34
+ ensure
35
+ File.umask(old)
36
+ end
37
+ end
38
+ private :umask_close
39
+
40
+ def env
41
+ unless File.file?(CONFIG_FILE)
42
+ puts "ERROR: Config file not found: #{CONFIG_FILE}"
43
+ return
44
+ end
45
+ puts "OK: Config file exists: #{CONFIG_FILE.realpath}"
46
+
47
+ puts "OK: Auth name(gdata_auth): #{@config["gdata_auth"]}"
48
+
49
+ if @config["gdata_files"]
50
+ puts "OK: gdata_files"
51
+ @config["gdata_files"].to_a.each do |fileconf|
52
+ puts <<"EOT"
53
+ - title: "#{fileconf["title"]}"
54
+ key: #{fileconf["key"]}
55
+ file: #{fileconf["file"]}
56
+ EOT
57
+ end
58
+ else
59
+ puts <<'EOT'
60
+ ERROR: gdata_files doesn't set.
61
+
62
+ Configulation sample (add these lines to _dir.smc):
63
+ ----------------------------------------------------------------
64
+ gdata_auth: default
65
+ gdata_files:
66
+ - key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
67
+ file: _smc/data/sample1.csv
68
+ - key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
69
+ file: _smc/data/sample2.csv
70
+ ----------------------------------------------------------------
71
+ You can get document keys using 'gdata:keys' task.
72
+
73
+ EOT
74
+ end
75
+
76
+ unless AUTH_ROOT.exist?
77
+ umask_close do
78
+ Dir.mkdir(AUTH_ROOT)
79
+ puts "OK: auth dir created: #{AUTH_ROOT}"
80
+ end
81
+ end
82
+ if AUTH_ROOT.stat.mode & 077 != 0
83
+ puts "ERROR: Close group/other permission for security: #{AUTH_ROOT}"
84
+ end
85
+
86
+ afile = auth_file
87
+ if afile.file?
88
+ if afile.stat.mode & 077 != 0
89
+ puts "ERROR: Close group/other permission for security: #{afile}"
90
+ else
91
+ puts "OK: auth file exists: #{afile}"
92
+ end
93
+ else
94
+ puts "ERROR: auth file doesn't exist. execute gdata:login task.: #{afile}"
95
+ end
96
+ end
97
+
98
+ def login
99
+ umask_close { _login }
100
+ end
101
+
102
+ def _login
103
+ unless AUTH_ROOT.directory?
104
+ Dir.mkdir(AUTH_ROOT)
105
+ puts "OK: auth dir created: #{AUTH_ROOT}"
106
+ end
107
+ check_permission(AUTH_ROOT)
108
+
109
+ if auth_file.exist?
110
+ auth = YAML.load_file(auth_file)
111
+ end
112
+ auth ||= {}
113
+ if auth["email"] || auth["pass"]
114
+ puts "ERROR: using email/pass in the auth file: #{auth_file}"
115
+ return
116
+ end
117
+
118
+ (email,pass) = login_prompt_highline
119
+ c = GData::Client::DocList.new
120
+ auth["doclist"] = c.clientlogin(email, pass)
121
+ c = GData::Client::Spreadsheets.new
122
+ auth["spreadsheets"] = c.clientlogin(email, pass)
123
+
124
+ open(auth_file, "w") do |io|
125
+ io << auth.to_yaml
126
+ end
127
+ puts "OK: Login token saved: #{auth_file}"
128
+ end
129
+ private :_login
130
+
131
+ def login_prompt_highline
132
+ email = ask("Email: ")
133
+ pass = ask("Password: ") { |q| q.echo = '*' }
134
+ return [email, pass]
135
+ end
136
+ private :login_prompt_highline
137
+
138
+ def check_permission(path)
139
+ if path.stat.mode & 077 != 0
140
+ raise "Close group/other permission for security: #{path}"
141
+ end
142
+ end
143
+
144
+ def list
145
+ check_permission(auth_file.parent)
146
+ check_permission(auth_file)
147
+
148
+ c = GData::Client::DocList.new
149
+ auth = YAML.load_file(auth_file)
150
+
151
+ if auth["email"].to_s.empty?
152
+ c.auth_handler = GData::Auth::ClientLogin.new("writely")
153
+ c.auth_handler.token = auth["doclist"]
154
+ else
155
+ c.clientlogin(auth["email"], auth["pass"])
156
+ end
157
+
158
+ xml = c.get("http://docs.google.com/feeds/documents/private/full/-/spreadsheet").to_xml
159
+ xml.elements.each("entry") do |e|
160
+ key = e.elements["gd:resourceId"].text.to_s.split(/:/)[1]
161
+ title = e.elements["title"].text
162
+ puts <<"EOT"
163
+ - title: "#{title}"
164
+ key: #{key}
165
+ file: _smc/data/sample.csv
166
+
167
+ EOT
168
+ end
169
+ end
170
+
171
+ def export
172
+ check_permission(auth_file.parent)
173
+ check_permission(auth_file)
174
+
175
+ c = GData::Client::Spreadsheets.new
176
+ auth = YAML.load_file(auth_file) || {}
177
+
178
+ if auth["email"].to_s.empty?
179
+ if auth["spreadsheets"].to_s.empty?
180
+ puts "ERROR: execute gdata:login task."
181
+ return
182
+ end
183
+ c.auth_handler = GData::Auth::ClientLogin.new("wise")
184
+ c.auth_handler.token = auth["spreadsheets"]
185
+ else
186
+ c.clientlogin(auth["email"], auth["pass"])
187
+ end
188
+
189
+ @config["gdata_files"].each do |fdata|
190
+ next if fdata["key"] =~ /^x{44}$/
191
+ data = c.get("http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=#{fdata["key"]}&exportFormat=csv")
192
+ contents = data.body
193
+ open(File.dirname(__FILE__) + "/../../" + fdata["file"], "w") do |io|
194
+ io << contents
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ namespace :gdata do
201
+
202
+ desc "show Google Data API configuration."
203
+ task :env do
204
+ exporter = GDataExporter.new
205
+ exporter.env
206
+ end
207
+
208
+ desc "login Google Data API."
209
+ task :login do
210
+ exporter = GDataExporter.new
211
+ exporter.login
212
+ end
213
+
214
+ desc "list all Google Spreadsheets."
215
+ task :list do
216
+ exporter = GDataExporter.new
217
+ exporter.list
218
+ end
219
+
220
+ desc "export Google Spreadsheets as CSV."
221
+ task :export do
222
+ exporter = GDataExporter.new
223
+ exporter.export
224
+ end
225
+
226
+
227
+ end
@@ -1,6 +1,5 @@
1
1
  Dir.chdir(File.dirname(__FILE__) + "/..")
2
2
 
3
- require 'pathname'
4
3
  require 'rubygems'
5
4
  require 'smallcage'
6
5
 
data/smallcage.gemspec CHANGED
@@ -5,13 +5,13 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{smallcage}
8
- s.version = "0.1.9"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["SAITO Toshihiro", "gommmmmm", "KOSEKI Kengo"]
12
- s.date = %q{2009-11-09}
12
+ s.date = %q{2009-11-13}
13
13
  s.default_executable = %q{smc}
14
- s.description = %q{Lightweight CMS package.}
14
+ s.description = %q{SmallCage is a simple, but powerful website generator. It converts content and template files, which has common elements in a website, to a plain, static website. No database, no application container, and no repeat in many pages is needed. You can keep your site well with very little work.}
15
15
  s.email = %q{smallcage@googlegroups.com}
16
16
  s.executables = ["smc"]
17
17
  s.extra_rdoc_files = [
@@ -57,6 +57,7 @@ Gem::Specification.new do |s|
57
57
  "project/cache/_smc/filters/cache_filter.rb",
58
58
  "project/cache/_smc/filters/filters.yml",
59
59
  "project/cache/_smc/rakelib/cache.rake",
60
+ "project/gdata/_smc/rakelib/gdata.rake",
60
61
  "project/lang/_smc/helpers/lang_helper.rb",
61
62
  "project/lang/_smc/templates/other_lang.rhtml",
62
63
  "project/news/_smc/helpers/news_helper.rb",
@@ -147,7 +148,7 @@ Gem::Specification.new do |s|
147
148
  s.rdoc_options = ["--charset=UTF-8"]
148
149
  s.require_paths = ["lib"]
149
150
  s.rubygems_version = %q{1.3.5}
150
- s.summary = %q{Lightweight CMS package.}
151
+ s.summary = %q{simple website generator}
151
152
  s.test_files = [
152
153
  "spec/document_path_spec.rb",
153
154
  "spec/export_spec.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smallcage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SAITO Toshihiro
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2009-11-09 00:00:00 +09:00
14
+ date: 2009-11-13 00:00:00 +09:00
15
15
  default_executable: smc
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -24,7 +24,7 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: 1.2.9
26
26
  version:
27
- description: Lightweight CMS package.
27
+ description: SmallCage is a simple, but powerful website generator. It converts content and template files, which has common elements in a website, to a plain, static website. No database, no application container, and no repeat in many pages is needed. You can keep your site well with very little work.
28
28
  email: smallcage@googlegroups.com
29
29
  executables:
30
30
  - smc
@@ -72,6 +72,7 @@ files:
72
72
  - project/cache/_smc/filters/cache_filter.rb
73
73
  - project/cache/_smc/filters/filters.yml
74
74
  - project/cache/_smc/rakelib/cache.rake
75
+ - project/gdata/_smc/rakelib/gdata.rake
75
76
  - project/lang/_smc/helpers/lang_helper.rb
76
77
  - project/lang/_smc/templates/other_lang.rhtml
77
78
  - project/news/_smc/helpers/news_helper.rb
@@ -184,7 +185,7 @@ rubyforge_project:
184
185
  rubygems_version: 1.3.5
185
186
  signing_key:
186
187
  specification_version: 3
187
- summary: Lightweight CMS package.
188
+ summary: simple website generator
188
189
  test_files:
189
190
  - spec/document_path_spec.rb
190
191
  - spec/export_spec.rb