TwP-webby 0.9.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.
Files changed (174) hide show
  1. data/History.txt +176 -0
  2. data/Manifest.txt +173 -0
  3. data/README.txt +92 -0
  4. data/Rakefile +50 -0
  5. data/bin/webby +8 -0
  6. data/bin/webby-gen +8 -0
  7. data/examples/blog/Sitefile +7 -0
  8. data/examples/blog/tasks/blog.rake +72 -0
  9. data/examples/blog/templates/atom_feed.erb +40 -0
  10. data/examples/blog/templates/blog/month.erb +22 -0
  11. data/examples/blog/templates/blog/post.erb +16 -0
  12. data/examples/blog/templates/blog/year.erb +22 -0
  13. data/examples/presentation/Sitefile +10 -0
  14. data/examples/presentation/content/css/uv/twilight.css +137 -0
  15. data/examples/presentation/content/presentation/_sample_code.txt +10 -0
  16. data/examples/presentation/content/presentation/index.txt +63 -0
  17. data/examples/presentation/content/presentation/s5/blank.gif +0 -0
  18. data/examples/presentation/content/presentation/s5/bodybg.gif +0 -0
  19. data/examples/presentation/content/presentation/s5/framing.css +23 -0
  20. data/examples/presentation/content/presentation/s5/iepngfix.htc +42 -0
  21. data/examples/presentation/content/presentation/s5/opera.css +7 -0
  22. data/examples/presentation/content/presentation/s5/outline.css +15 -0
  23. data/examples/presentation/content/presentation/s5/pretty.css +86 -0
  24. data/examples/presentation/content/presentation/s5/print.css +1 -0
  25. data/examples/presentation/content/presentation/s5/s5-core.css +9 -0
  26. data/examples/presentation/content/presentation/s5/slides.css +3 -0
  27. data/examples/presentation/content/presentation/s5/slides.js +553 -0
  28. data/examples/presentation/layouts/presentation.txt +43 -0
  29. data/examples/presentation/templates/_code_partial.erb +13 -0
  30. data/examples/presentation/templates/presentation.erb +40 -0
  31. data/examples/tumblog/Sitefile +9 -0
  32. data/examples/tumblog/content/css/tumblog.css +308 -0
  33. data/examples/tumblog/content/images/tumblog/permalink.gif +0 -0
  34. data/examples/tumblog/content/images/tumblog/rss.gif +0 -0
  35. data/examples/tumblog/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
  36. data/examples/tumblog/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
  37. data/examples/tumblog/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
  38. data/examples/tumblog/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
  39. data/examples/tumblog/content/tumblog/200807/up-a-tree/index.txt +13 -0
  40. data/examples/tumblog/content/tumblog/index.txt +37 -0
  41. data/examples/tumblog/content/tumblog/rss.txt +37 -0
  42. data/examples/tumblog/layouts/tumblog/default.txt +44 -0
  43. data/examples/tumblog/layouts/tumblog/post.txt +15 -0
  44. data/examples/tumblog/lib/tumblog_helper.rb +32 -0
  45. data/examples/tumblog/tasks/tumblog.rake +30 -0
  46. data/examples/tumblog/templates/atom_feed.erb +40 -0
  47. data/examples/tumblog/templates/tumblog/conversation.erb +12 -0
  48. data/examples/tumblog/templates/tumblog/link.erb +10 -0
  49. data/examples/tumblog/templates/tumblog/photo.erb +13 -0
  50. data/examples/tumblog/templates/tumblog/post.erb +12 -0
  51. data/examples/tumblog/templates/tumblog/quote.erb +11 -0
  52. data/examples/webby/Sitefile +19 -0
  53. data/examples/webby/content/communicate/index.txt +28 -0
  54. data/examples/webby/content/css/background.gif +0 -0
  55. data/examples/webby/content/css/blueprint/print.css +76 -0
  56. data/examples/webby/content/css/blueprint/screen.css +696 -0
  57. data/examples/webby/content/css/coderay.css +96 -0
  58. data/examples/webby/content/css/site.css +196 -0
  59. data/examples/webby/content/css/uv/twilight.css +137 -0
  60. data/examples/webby/content/index.txt +37 -0
  61. data/examples/webby/content/learn/index.txt +28 -0
  62. data/examples/webby/content/reference/index.txt +204 -0
  63. data/examples/webby/content/release-notes/rel-0-9-0/index.txt +73 -0
  64. data/examples/webby/content/robots.txt +6 -0
  65. data/examples/webby/content/script/jquery.corner.js +152 -0
  66. data/examples/webby/content/script/jquery.js +31 -0
  67. data/examples/webby/content/sitemap.txt +31 -0
  68. data/examples/webby/content/tips_and_tricks/index.txt +96 -0
  69. data/examples/webby/content/tutorial/index.txt +131 -0
  70. data/examples/webby/content/user-manual/index.txt +419 -0
  71. data/examples/webby/layouts/default.txt +49 -0
  72. data/examples/webby/templates/page.erb +10 -0
  73. data/examples/website/Sitefile +7 -0
  74. data/examples/website/content/css/blueprint/License.txt +21 -0
  75. data/examples/website/content/css/blueprint/Readme.txt +100 -0
  76. data/examples/website/content/css/blueprint/compressed/print.css +76 -0
  77. data/examples/website/content/css/blueprint/compressed/screen.css +696 -0
  78. data/examples/website/content/css/blueprint/lib/forms.css +45 -0
  79. data/examples/website/content/css/blueprint/lib/grid.css +193 -0
  80. data/examples/website/content/css/blueprint/lib/grid.png +0 -0
  81. data/examples/website/content/css/blueprint/lib/ie.css +30 -0
  82. data/examples/website/content/css/blueprint/lib/reset.css +39 -0
  83. data/examples/website/content/css/blueprint/lib/typography.css +116 -0
  84. data/examples/website/content/css/blueprint/plugins/buttons/Readme +31 -0
  85. data/examples/website/content/css/blueprint/plugins/buttons/buttons.css +97 -0
  86. data/examples/website/content/css/blueprint/plugins/buttons/icons/cross.png +0 -0
  87. data/examples/website/content/css/blueprint/plugins/buttons/icons/key.png +0 -0
  88. data/examples/website/content/css/blueprint/plugins/buttons/icons/tick.png +0 -0
  89. data/examples/website/content/css/blueprint/plugins/css-classes/Readme +14 -0
  90. data/examples/website/content/css/blueprint/plugins/css-classes/css-classes.css +24 -0
  91. data/examples/website/content/css/blueprint/plugins/fancy-type/Readme +22 -0
  92. data/examples/website/content/css/blueprint/plugins/fancy-type/fancy-type-compressed.css +5 -0
  93. data/examples/website/content/css/blueprint/plugins/fancy-type/fancy-type.css +74 -0
  94. data/examples/website/content/css/blueprint/print.css +68 -0
  95. data/examples/website/content/css/blueprint/screen.css +22 -0
  96. data/examples/website/content/css/coderay.css +111 -0
  97. data/examples/website/content/css/site.css +67 -0
  98. data/examples/website/content/index.txt +19 -0
  99. data/examples/website/layouts/default.txt +58 -0
  100. data/examples/website/lib/breadcrumbs.rb +28 -0
  101. data/examples/website/templates/_partial.erb +10 -0
  102. data/examples/website/templates/page.erb +18 -0
  103. data/examples/website/templates/presentation.erb +40 -0
  104. data/lib/webby/apps/generator.rb +283 -0
  105. data/lib/webby/apps/main.rb +221 -0
  106. data/lib/webby/apps.rb +12 -0
  107. data/lib/webby/auto_builder.rb +83 -0
  108. data/lib/webby/builder.rb +183 -0
  109. data/lib/webby/core_ext/enumerable.rb +11 -0
  110. data/lib/webby/core_ext/hash.rb +28 -0
  111. data/lib/webby/core_ext/kernel.rb +21 -0
  112. data/lib/webby/core_ext/string.rb +163 -0
  113. data/lib/webby/core_ext/time.rb +9 -0
  114. data/lib/webby/filters/basepath.rb +97 -0
  115. data/lib/webby/filters/erb.rb +9 -0
  116. data/lib/webby/filters/haml.rb +18 -0
  117. data/lib/webby/filters/markdown.rb +16 -0
  118. data/lib/webby/filters/outline.rb +309 -0
  119. data/lib/webby/filters/sass.rb +17 -0
  120. data/lib/webby/filters/slides.rb +56 -0
  121. data/lib/webby/filters/textile.rb +16 -0
  122. data/lib/webby/filters/tidy.rb +76 -0
  123. data/lib/webby/filters.rb +91 -0
  124. data/lib/webby/helpers/capture_helper.rb +141 -0
  125. data/lib/webby/helpers/coderay_helper.rb +69 -0
  126. data/lib/webby/helpers/graphviz_helper.rb +136 -0
  127. data/lib/webby/helpers/tag_helper.rb +65 -0
  128. data/lib/webby/helpers/tex_img_helper.rb +133 -0
  129. data/lib/webby/helpers/ultraviolet_helper.rb +63 -0
  130. data/lib/webby/helpers/url_helper.rb +235 -0
  131. data/lib/webby/helpers.rb +30 -0
  132. data/lib/webby/link_validator.rb +152 -0
  133. data/lib/webby/renderer.rb +379 -0
  134. data/lib/webby/resources/db.rb +251 -0
  135. data/lib/webby/resources/file.rb +221 -0
  136. data/lib/webby/resources/layout.rb +63 -0
  137. data/lib/webby/resources/page.rb +118 -0
  138. data/lib/webby/resources/partial.rb +79 -0
  139. data/lib/webby/resources/resource.rb +160 -0
  140. data/lib/webby/resources/static.rb +52 -0
  141. data/lib/webby/resources.rb +96 -0
  142. data/lib/webby/stelan/mktemp.rb +135 -0
  143. data/lib/webby/stelan/paginator.rb +150 -0
  144. data/lib/webby/stelan/spawner.rb +339 -0
  145. data/lib/webby/tasks/build.rake +27 -0
  146. data/lib/webby/tasks/create.rake +22 -0
  147. data/lib/webby/tasks/deploy.rake +22 -0
  148. data/lib/webby/tasks/growl.rake +15 -0
  149. data/lib/webby/tasks/heel.rake +28 -0
  150. data/lib/webby/tasks/validate.rake +19 -0
  151. data/lib/webby.rb +227 -0
  152. data/spec/core_ext/hash_spec.rb +47 -0
  153. data/spec/core_ext/string_spec.rb +110 -0
  154. data/spec/core_ext/time_spec.rb +19 -0
  155. data/spec/spec.opts +1 -0
  156. data/spec/spec_helper.rb +14 -0
  157. data/spec/webby/apps/generator_spec.rb +111 -0
  158. data/spec/webby/apps/main_spec.rb +75 -0
  159. data/spec/webby/helpers/capture_helper_spec.rb +56 -0
  160. data/spec/webby/resources/file_spec.rb +104 -0
  161. data/spec/webby/resources_spec.rb +17 -0
  162. data/tasks/ann.rake +81 -0
  163. data/tasks/bones.rake +21 -0
  164. data/tasks/gem.rake +126 -0
  165. data/tasks/git.rake +41 -0
  166. data/tasks/manifest.rake +49 -0
  167. data/tasks/notes.rake +28 -0
  168. data/tasks/post_load.rake +39 -0
  169. data/tasks/rdoc.rake +51 -0
  170. data/tasks/rubyforge.rake +57 -0
  171. data/tasks/setup.rb +268 -0
  172. data/tasks/spec.rake +55 -0
  173. data/tasks/website.rake +38 -0
  174. metadata +289 -0
@@ -0,0 +1,79 @@
1
+ require Webby.libpath(*%w[webby resources resource])
2
+
3
+ module Webby::Resources
4
+
5
+ # A Partial is a file in the content folder whose filename starts with an
6
+ # underscore "_" character. Partials contain text that can be included into
7
+ # other pages. Partials are not standalone pages, and they will never
8
+ # correspond directly to an output file.
9
+ #
10
+ # Partials can contain YAML meta-data at the top of the file. This
11
+ # information is only used to determine the filters to apply to the
12
+ # partial. If there is no meta-data, then the partial text is used "as is"
13
+ # without any processing by the Webby rendering engine.
14
+ #
15
+ class Partial < Resource
16
+
17
+ # call-seq:
18
+ # Partial.new( path )
19
+ #
20
+ # Creates a new Partial object given the full path to the partial file.
21
+ # Partial filenames start with an underscore (this is an enforced
22
+ # convention).
23
+ #
24
+ def initialize( fn )
25
+ super
26
+
27
+ @mdata = ::Webby::Resources::File.meta_data(@path)
28
+ @mdata ||= {}
29
+ @mdata.sanitize!
30
+ end
31
+
32
+ # call-seq:
33
+ # dirty? => true or false
34
+ #
35
+ # Returns +true+ if this resource is newer than its corresponding output
36
+ # product. The resource needs to be rendered (if a page or layout) or
37
+ # copied (if a static file) to the output directory.
38
+ #
39
+ def dirty?
40
+ return @mdata['dirty'] if @mdata.has_key? 'dirty'
41
+
42
+ # if the destination file does not exist, then we are dirty
43
+ return true unless test(?e, destination)
44
+
45
+ # if this file's mtime is larger than the destination file's
46
+ # mtime, then we are dirty
47
+ dirty = @mtime > ::File.mtime(destination)
48
+ return dirty if dirty
49
+
50
+ # if we got here, then we are not dirty
51
+ false
52
+ end
53
+
54
+ # call-seq:
55
+ # destination => string
56
+ #
57
+ # The output file destination for the partial. This is the ".cairn" file in
58
+ # the output folder. It is used to determine if the partial is newer than
59
+ # the build products.
60
+ #
61
+ def destination
62
+ ::Webby.cairn
63
+ end
64
+
65
+ alias :extension :ext
66
+
67
+ # call-seq:
68
+ # url => nil
69
+ #
70
+ # Partials do not have a URL. This method will alwasy return +nil+.
71
+ #
72
+ def url
73
+ nil
74
+ end
75
+
76
+ end # class Partial
77
+ end # module Webby::Resources
78
+
79
+ # EOF
@@ -0,0 +1,160 @@
1
+ unless defined? Webby::Resources::Resource
2
+
3
+ module Webby::Resources
4
+
5
+ # A Webby::Resource is any file that can be found in the content directory
6
+ # or in the layout directory. This class contains information about the
7
+ # resources available to Webby.
8
+ #
9
+ class Resource
10
+
11
+ instance_methods.each do |m|
12
+ undef_method(m) unless m =~ %r/\A__|\?$/ ||
13
+ m == 'class'
14
+ end
15
+
16
+ # The full path to the resource file
17
+ attr_reader :path
18
+
19
+ # The directory of the resource excluding the content directory
20
+ attr_reader :dir
21
+
22
+ # The resource filename excluding path and extension
23
+ attr_reader :filename
24
+
25
+ # Extesion of the resource file
26
+ attr_reader :ext
27
+
28
+ # Resource file modification time
29
+ attr_reader :mtime
30
+
31
+ # call-seq:
32
+ # Resource.new( filename ) => resource
33
+ #
34
+ # Creates a new resource object given the _filename_.
35
+ #
36
+ def initialize( fn )
37
+ @path = fn
38
+ @dir = ::Webby::Resources::File.dirname(@path)
39
+ @filename = ::Webby::Resources::File.basename(@path)
40
+ @ext = ::Webby::Resources::File.extname(@path)
41
+ @mtime = ::File.mtime @path
42
+
43
+ @mdata = @@mdata ||= {}
44
+ end
45
+
46
+ # call-seq:
47
+ # equal?( other ) => true or false
48
+ #
49
+ # Returns +true+ if the path of this resource is equivalent to the path of
50
+ # the _other_ resource. Returns +false+ if this is not the case.
51
+ #
52
+ def equal?( other )
53
+ return false unless other.kind_of? ::Webby::Resources::Resource
54
+ @path == other.path
55
+ end
56
+ alias :== :equal?
57
+ alias :eql? :equal?
58
+
59
+ # call-seq:
60
+ # resource <=> other => -1, 0, +1, or nil
61
+ #
62
+ # Resource comparison operates on the full path of the resource objects
63
+ # and uses the standard String comparison operator. Returns +nil+ if
64
+ # _other_ is not a Resource instance.
65
+ #
66
+ def <=>( other )
67
+ return unless other.kind_of? ::Webby::Resources::Resource
68
+ @path <=> other.path
69
+ end
70
+
71
+ # call-seq:
72
+ # resource[key] => value or nil
73
+ #
74
+ # Returns the value associated with the given meta-data key. Key is
75
+ # converted into a string.
76
+ #
77
+ def []( key )
78
+ @mdata[key.to_s]
79
+ end
80
+
81
+ # call-seq:
82
+ # resource[key] = value
83
+ #
84
+ # Sets the given meta-data key to the value. Key is converted into a
85
+ # string.
86
+ #
87
+ def []=( key, value )
88
+ @mdata[key.to_s] = value
89
+ end
90
+
91
+ # call-seq:
92
+ # method_missing( symbol [, *args, &block] ) => result
93
+ #
94
+ # Invoked by Ruby when a message is sent to the resource that it cannot
95
+ # handle. The default behavior is to convert _symbol_ to a string and
96
+ # search for that string in the resource's meta-data. If found, the
97
+ # meta-data item is returned; otherwise, +nil+ is returned.
98
+ #
99
+ def method_missing( name, *a, &b )
100
+ @mdata[name.to_s]
101
+ end
102
+
103
+ # call-seq:
104
+ # dirty? => true or false
105
+ #
106
+ # Returns +true+ if this resource is newer than its corresponding output
107
+ # product. The resource needs to be rendered (if a page or layout) or
108
+ # copied (if a static file) to the output directory.
109
+ #
110
+ def dirty?
111
+ return @mdata['dirty'] if @mdata.has_key? 'dirty'
112
+
113
+ # if the destination file does not exist, then we are dirty
114
+ return true unless test(?e, destination)
115
+
116
+ # if this file's mtime is larger than the destination file's
117
+ # mtime, then we are dirty
118
+ dirty = @mtime > ::File.mtime(destination)
119
+ return dirty if dirty
120
+
121
+ # check to see if the layout is dirty, and if it is then we
122
+ # are dirty, too
123
+ if @mdata.has_key? 'layout'
124
+ lyt = ::Webby::Resources.find_layout(@mdata['layout'])
125
+ unless lyt.nil?
126
+ return true if lyt.dirty?
127
+ end
128
+ end
129
+
130
+ # if we got here, then we are not dirty
131
+ false
132
+ end
133
+
134
+ # call-seq
135
+ # url => string or nil
136
+ #
137
+ # Returns a string suitable for use as a URL linking to this page. Nil
138
+ # is returned for layouts.
139
+ #
140
+ def url
141
+ return @url if defined? @url and @url
142
+ @url = destination.sub(::Webby.site.output_dir, '')
143
+ end
144
+
145
+ # :stopdoc:
146
+ def destination
147
+ raise NotImplementedError
148
+ end
149
+
150
+ def extension
151
+ raise NotImplementedError
152
+ end
153
+ # :startdoc:
154
+
155
+ end # class Resource
156
+ end # module Webby::Resources
157
+
158
+ end # unless defined?
159
+
160
+ # EOF
@@ -0,0 +1,52 @@
1
+ require Webby.libpath(*%w[webby resources resource])
2
+
3
+ module Webby::Resources
4
+
5
+ # A Static resource is any file in the content folder that does not
6
+ # contain YAML meta-data at the top of the file and does not start with
7
+ # and underscore "_" character (those are partials). Static resources will
8
+ # be copied as-is from the content directory to the output directory.
9
+ #
10
+ class Static < Resource
11
+
12
+ # call-seq:
13
+ # render => string
14
+ #
15
+ # Returns the contents of the file.
16
+ #
17
+ def render
18
+ ::File.read(path)
19
+ end
20
+
21
+ # call-seq:
22
+ # dirty? => true or false
23
+ #
24
+ # Returns +true+ if this static file is newer than its corresponding output
25
+ # product. The static file needs to be copied to the output directory.
26
+ #
27
+ def dirty?
28
+ return true unless test(?e, destination)
29
+ @mtime > ::File.mtime(destination)
30
+ end
31
+
32
+ # call-seq:
33
+ # destination => string
34
+ #
35
+ # Returns the path in the output directory where the static file should
36
+ # be copied. This path is used to determine if the static file is dirty
37
+ # and in need of copying to the output file.
38
+ #
39
+ def destination
40
+ return @dest if defined? @dest and @dest
41
+
42
+ @dest = ::File.join(::Webby.site.output_dir, dir, filename)
43
+ @dest << '.' << @ext if @ext and !@ext.empty?
44
+ @dest
45
+ end
46
+
47
+ alias :extension :ext
48
+
49
+ end # class Layout
50
+ end # module Webby::Resources
51
+
52
+ # EOF
@@ -0,0 +1,96 @@
1
+ module Webby::Resources
2
+
3
+ class << self
4
+ # Returns the pages hash object.
5
+ #
6
+ def pages
7
+ @pages ||= ::Webby::Resources::DB.new
8
+ end
9
+
10
+ # Returns the layouts hash object.
11
+ #
12
+ def layouts
13
+ @layouts ||= ::Webby::Resources::DB.new
14
+ end
15
+
16
+ # Returns the partials hash object.
17
+ #
18
+ def partials
19
+ @partials ||= ::Webby::Resources::DB.new
20
+ end
21
+
22
+ # Clear the contents of the +layouts+, +pages+ and +partials+ hash
23
+ # objects.
24
+ #
25
+ def clear
26
+ self.pages.clear
27
+ self.layouts.clear
28
+ self.partials.clear
29
+ end
30
+
31
+ # call-seq:
32
+ # Resources.new( filename )
33
+ #
34
+ #
35
+ def new( fn )
36
+ # normalize the path
37
+ fn = self.path(fn)
38
+
39
+ # see if we are dealing with a layout
40
+ if %r/\A#{::Webby.site.layout_dir}\//o =~ fn
41
+ r = ::Webby::Resources::Layout.new(fn)
42
+ self.layouts << r
43
+ return r
44
+ end
45
+
46
+ # see if we are dealing with a partial
47
+ filename = ::Webby::Resources::File.basename(fn)
48
+ if %r/\A_/o =~ filename
49
+ r = ::Webby::Resources::Partial.new(fn)
50
+ self.partials << r
51
+ return r
52
+ end
53
+
54
+ # see if we are dealing with a static resource
55
+ meta = ::Webby::Resources::File.meta_data(fn)
56
+ if meta.nil?
57
+ r = ::Webby::Resources::Static.new(fn)
58
+ self.pages << r
59
+ return r
60
+ end
61
+
62
+ # this is a renderable page
63
+ r = ::Webby::Resources::Page.new(fn)
64
+ self.pages << r
65
+ return r
66
+ end
67
+
68
+ # Returns a normalized path for the given filename.
69
+ #
70
+ def path( filename )
71
+ filename.sub(%r/\A(?:\.\/|\/)/o, '').freeze
72
+ end
73
+
74
+ # Returns the layout resource corresponding to the given _filename_ or
75
+ # +nil+ if no layout exists under that filename.
76
+ #
77
+ def find_layout( filename )
78
+ return if filename.nil?
79
+
80
+ fn = ::Webby::Resources::File.basename(filename)
81
+ dir = ::File.dirname(filename)
82
+ dir = '.' == dir ? '' : dir
83
+
84
+ layouts.find(:filename => fn, :in_directory => dir)
85
+
86
+ rescue RuntimeError
87
+ raise Webby::Error, "could not find layout #{filename.inspect}"
88
+ end
89
+
90
+ end # class << self
91
+
92
+ end # module Webby::Resources
93
+
94
+ Webby.require_all_libs_relative_to(__FILE__)
95
+
96
+ # EOF
@@ -0,0 +1,135 @@
1
+ # :stopdoc:
2
+ # Skeleton module for the 'mktemp' routine.
3
+ #
4
+ # Ideally, one would do this in their code to import the "mktemp" call
5
+ # directly into their current namespace:
6
+ #
7
+ # require 'mktemp'
8
+ # include MkTemp
9
+ # # do something with mktemp()
10
+ #
11
+ #
12
+ # It is recommended that you look at the documentation for the mktemp()
13
+ # call directly for specific usage.
14
+ #
15
+ #--
16
+ #
17
+ # The compilation of software known as mktemp.rb is distributed under the
18
+ # following terms:
19
+ # Copyright (C) 2005-2006 Erik Hollensbe. All rights reserved.
20
+ #
21
+ # Redistribution and use in source form, with or without
22
+ # modification, are permitted provided that the following conditions
23
+ # are met:
24
+ # 1. Redistributions of source code must retain the above copyright
25
+ # notice, this list of conditions and the following disclaimer.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
28
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30
+ # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
31
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33
+ # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
+ # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37
+ # SUCH DAMAGE.
38
+ #
39
+ #++
40
+
41
+ module Webby
42
+ module MkTemp
43
+ VALID_TMPNAM_CHARS = (?a..?z).to_a + (?A..?Z).to_a
44
+
45
+ #
46
+ # This routine just generates a temporary file similar to the
47
+ # routines from 'mktemp'. A trailing series of 'X' characters will
48
+ # be transformed into a randomly-generated set of alphanumeric
49
+ # characters.
50
+ #
51
+ # This routine performs no file testing, at all. It is not suitable
52
+ # for anything beyond that.
53
+ #
54
+
55
+ def tmpnam(filename)
56
+ m = filename.match(/(X*)$/)
57
+
58
+ retnam = filename.dup
59
+
60
+ if m[1]
61
+ mask = ""
62
+ m[1].length.times { mask += VALID_TMPNAM_CHARS[rand(52)].chr }
63
+ retnam.sub!(/(X*)$/, mask)
64
+ end
65
+
66
+ return retnam
67
+ end
68
+
69
+ module_function :tmpnam
70
+
71
+ #
72
+ # This routine works similarly to mkstemp(3) in that it gets a new
73
+ # file, and returns a file handle for that file. The mask parameter
74
+ # determines whether or not to process the filename as a mask by
75
+ # calling the tmpnam() routine in this module. This routine will
76
+ # continue until it finds a valid filename, which may not do what
77
+ # you expect.
78
+ #
79
+ # While all attempts have been made to keep this as secure as
80
+ # possible, due to a few problems with Ruby's file handling code, we
81
+ # are required to allow a few concessions. If a 0-length file is
82
+ # created before we attempt to create ours, we have no choice but to
83
+ # accept it. Do not rely on this code for any expected level of
84
+ # security, even though we have taken all the measures we can to
85
+ # handle that situation.
86
+ #
87
+
88
+ def mktemp(filename, mask=true)
89
+ fh = nil
90
+
91
+ begin
92
+ loop do
93
+ fn = mask ? tmpnam(filename) : filename
94
+
95
+ if File.exist? fn
96
+ fail "Unable to create a temporary filename" unless mask
97
+ next
98
+ end
99
+
100
+ fh = File.new(fn, "a", 0600)
101
+ fh.seek(0, IO::SEEK_END)
102
+ break if fh.pos == 0
103
+
104
+ fail "Unable to create a temporary filename" unless mask
105
+ fh.close
106
+ end
107
+ rescue Exception => e
108
+ # in the case that we hit a locked file...
109
+ fh.close if fh
110
+ raise e unless mask
111
+ end
112
+
113
+ return fh
114
+ end
115
+
116
+ module_function :mktemp
117
+
118
+ #
119
+ # Create a directory. If mask is true (default), it will use the
120
+ # random name generation rules from the tmpnam() call in this
121
+ # module.
122
+ #
123
+
124
+ def mktempdir(filename, mask=true)
125
+ fn = mask ? tmpnam(filename) : filename
126
+ Dir.mkdir(fn)
127
+ return fn
128
+ end
129
+
130
+ module_function :mktempdir
131
+ end # module MkTemp
132
+ end # module Webby
133
+
134
+ # :startdoc:
135
+ # EOF
@@ -0,0 +1,150 @@
1
+ # This code was originally written by Bruce Williams, and it is available
2
+ # as the Paginator gem. I've added a few helper methods and modifications so
3
+ # it plays a little more nicely with Webby. Specifically, a Webby::Resource
4
+ # can be given to the Page and used to generate links to the previous and
5
+ # next pages.
6
+ #
7
+ # Many thanks to Bruce Williams for letting me use his work. Drop him a note
8
+ # of praise scribbled on the back of a $100 bill. He'd appreciate it.
9
+
10
+ require 'forwardable'
11
+
12
+ module Webby
13
+ class Paginator
14
+
15
+ include Enumerable
16
+
17
+ class ArgumentError < ::ArgumentError; end
18
+ class MissingCountError < ArgumentError; end
19
+ class MissingSelectError < ArgumentError; end
20
+
21
+ attr_reader :per_page, :count, :resource
22
+
23
+ # Instantiate a new Paginator object
24
+ #
25
+ # Provide:
26
+ # * A total count of the number of objects to paginate
27
+ # * The number of objects in each page
28
+ # * A block that returns the array of items
29
+ # * The block is passed the item offset
30
+ # (and the number of items to show per page, for
31
+ # convenience, if the arity is 2)
32
+ def initialize(count, per_page, resource, &select)
33
+ @count, @per_page, @resource = count, per_page, resource
34
+ unless select
35
+ raise MissingSelectError, "Must provide block to select data for each page"
36
+ end
37
+ @select = select
38
+ end
39
+
40
+ # Total number of pages
41
+ def number_of_pages
42
+ (@count / @per_page).to_i + (@count % @per_page > 0 ? 1 : 0)
43
+ end
44
+
45
+ # First page object
46
+ def first
47
+ page 1
48
+ end
49
+
50
+ # Last page object
51
+ def last
52
+ page number_of_pages
53
+ end
54
+
55
+ def each
56
+ 1.upto(number_of_pages) do |number|
57
+ yield page(number)
58
+ end
59
+ end
60
+
61
+ # Retrieve page object by number
62
+ def page(number)
63
+ number = (n = number.to_i) > 0 ? n : 1
64
+ Page.new(self, number, lambda {
65
+ offset = (number - 1) * @per_page
66
+ args = [offset]
67
+ args << @per_page if @select.arity == 2
68
+ @select.call(*args)
69
+ })
70
+ end
71
+
72
+ # Page object
73
+ #
74
+ # Retrieves items for a page and provides metadata about the position
75
+ # of the page in the paginator
76
+ class Page
77
+
78
+ include Enumerable
79
+
80
+ attr_reader :number, :pager, :url
81
+
82
+ def initialize(pager, number, select) #:nodoc:
83
+ @pager, @number = pager, number
84
+ @offset = (number - 1) * pager.per_page
85
+ @select = select
86
+
87
+ @pager.resource.number = (number == 1 ? nil : number)
88
+ @url = @pager.resource.url
89
+ end
90
+
91
+ # Retrieve the items for this page
92
+ # * Caches
93
+ def items
94
+ @items ||= @select.call
95
+ end
96
+
97
+ # Checks to see if there's a page before this one
98
+ def prev?
99
+ @number > 1
100
+ end
101
+
102
+ # Get previous page (if possible)
103
+ def prev
104
+ @pager.page(@number - 1) if prev?
105
+ end
106
+
107
+ # Checks to see if there's a page after this one
108
+ def next?
109
+ @number < @pager.number_of_pages
110
+ end
111
+
112
+ # Get next page (if possible)
113
+ def next
114
+ @pager.page(@number + 1) if next?
115
+ end
116
+
117
+ # The "item number" of the first item on this page
118
+ def first_item_number
119
+ 1 + @offset
120
+ end
121
+
122
+ # The "item number" of the last item on this page
123
+ def last_item_number
124
+ if next?
125
+ @offset + @pager.per_page
126
+ else
127
+ @pager.count
128
+ end
129
+ end
130
+
131
+ def ==(other) #:nodoc:
132
+ @pager == other.pager && self.number == other.number
133
+ end
134
+
135
+ def each(&block)
136
+ items.each(&block)
137
+ end
138
+
139
+ def method_missing(meth, *args, &block) #:nodoc:
140
+ if @pager.respond_to?(meth)
141
+ @pager.__send__(meth, *args, &block)
142
+ else
143
+ super
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ end # class Paginator
150
+ end # module Webby