soxer 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.
data/lib/soxer/main.rb ADDED
@@ -0,0 +1,241 @@
1
+ require 'sinatra/base'
2
+ require 'yaml'
3
+ require 'haml'
4
+ require 'uuid'
5
+
6
+
7
+ module Sinatra
8
+
9
+ #== Soxer, the web publishing tool.
10
+ #
11
+ # Soxer is a Sinatra module that adds additional methods to sinatra routes.
12
+ # These methods allow for route-to-yaml-file mapping in order to serve a set
13
+ # files from disk with minimal effort. By clever use of views and/or
14
+ # templating Soxer makes for a simple and effective web site creation tool.
15
+ # You can get more information about Soxer, including the latest version at
16
+ # http://soxer.mutsu.org
17
+ #
18
+ # Author: Toni Anzlovar (toni[at]formalibre.si)
19
+ # Copyright: Copyright (c) 1010 Toni Anzlovar, www.formalibre.si
20
+ # License: Distributed under the GPL licence. http://www.gnu.org/licenses/gpl.html
21
+ module Soxer
22
+
23
+ class Filereader
24
+ attr_accessor :filename
25
+
26
+ def settings
27
+ @s = Sinatra::Application
28
+ end
29
+
30
+ def get_content
31
+ self.settings
32
+ out = YAML.load_file( @filename )
33
+ add_date unless out['date']
34
+ add_id unless out['uuid']
35
+ out['url'] = @filename.gsub(/#{@s.root}\/#{@s.origin}(.+)\.yaml$/, "\\1" ).gsub(/(.+)\/index$/, "\\1" )
36
+ out['mtime'] = File.mtime( @filename )
37
+ out
38
+ end
39
+
40
+ private
41
+
42
+ def add_id
43
+ mtime = File.mtime( @filename )
44
+ File.open( @filename, 'r+' ) do |f|
45
+ out = "uuid: #{UUID.new.generate}\n"
46
+ out << f.read; f.pos = 0
47
+ f << out
48
+ end
49
+ File.utime( 0, mtime, @filename )
50
+ end
51
+
52
+ def add_date
53
+ mtime = File.mtime( @filename )
54
+ File.open( @filename, 'r+' ) do |f|
55
+ out = "date: #{mtime.xmlschema}\n"
56
+ out << f.read; f.pos = 0
57
+ f << out
58
+ end
59
+ end
60
+ end
61
+
62
+ class Urlreader
63
+ attr_accessor :url
64
+
65
+ def settings
66
+ @s = Sinatra::Application
67
+ end
68
+
69
+ def get_content
70
+ self.settings
71
+ fn = case true
72
+ when File.exist?( f = File.join( @s.root, @s.origin, @url+'.yaml' ) ) then f
73
+ when File.exist?( f = File.join( @s.root, @s.origin, @url+'/index.yaml' ) ) then f
74
+ else throw :halt, [404, "Document not found"]
75
+ end
76
+ out = Filereader.new
77
+ out.filename = fn
78
+ out.get_content
79
+ end
80
+ end
81
+
82
+ # === The "get_page", the document reading function
83
+ #
84
+ # Read the document in yaml format (with .yaml) ending directly mapped from
85
+ # the given parameter. Sinatra's *settings.route* and Soxers
86
+ # *settings.origin* are prefixed to the path in order to get an absolute
87
+ # filename. If the filename cannot be found, a directory by that name and
88
+ # an index file within are probed and read.
89
+ #
90
+ # If no parameter is given, the entire route is taken as the argument.
91
+ def get_page url=params[:splat][0]
92
+ out = Urlreader.new
93
+ out.url = url
94
+ out.get_content
95
+ end
96
+
97
+ #=== The "get_list" the document listing function
98
+ #
99
+ # The get_list function is the aggregator function. It reads all available
100
+ # yaml files (that map to urls directly) and outputs them according to the
101
+ # arguments you send it. Get_list only accepts 1 argument and a block.
102
+ #
103
+ #==== sort='desc'
104
+ # Direction of output. 'desc' (descending) or 'asc' (ascending)The default
105
+ # is 'desc'. If :sort conatins anything else, the output is unsorted.
106
+ #
107
+ #==== &block
108
+ # Block is a callback. Every file (in hash form) is passed to block and
109
+ # the block acts as the filter. :sort only takes the result and sorts it
110
+ # according to the first key/value pair, so hash order IS signifficant.
111
+ def get_list sort='desc', &block
112
+ pattern = File.join( settings.root, settings.origin, "**", "*.yaml" )
113
+ output = Dir.glob(pattern).map! do |f|
114
+ file = Filereader.new
115
+ file.filename = f
116
+ if block_given?
117
+ f = block.call file.get_content
118
+ end
119
+ end.compact
120
+ case sort
121
+ when 'desc' then output.sort!{|b,a| a.to_a[0] <=> b.to_a[0] }
122
+ when 'asc' then output.sort!{|a,b| a.to_a[0] <=> b.to_a[0] }
123
+ end
124
+ end
125
+
126
+ #=== The "sitemap" the sitemap generator
127
+ #
128
+ # This funnction accepts no arguments. It simply renders a sitemap file
129
+ # with all available urls from the site
130
+ def sitemap
131
+ template = File.read File.join( File.dirname(__FILE__), 'views', 'sitemap.haml' )
132
+ out = '<?xml version="1.0" encoding="UTF-8"?>'+"\n"
133
+ out << haml( template, :layout => false )
134
+ end
135
+
136
+
137
+ #=== The "atom" the atom feed generator
138
+ #
139
+ # This method accepts an author (which is the global feed's author)
140
+ # This is a required option, as the feed is only valid if it has at least
141
+ # the global author. If individual articles have a yaml field "author",
142
+ # the individual article's author is used for that article. In both cases,
143
+ # author is a hash consisting of values 'name', 'email', 'url', of which
144
+ # at least the 'name' should always be present.
145
+ #
146
+ #==== autor
147
+ # Hash of values as required by the Atom standard:
148
+ # 'name', 'email' and 'url'. Only name is reuired.
149
+ #
150
+ #==== &block
151
+ # Block is a callback. Every file (in hash form) is passed to block and
152
+ # the block acts as the filter. That way only pages which are returned by
153
+ # block are included in the feed
154
+ def atom author=author, &block
155
+ template = File.read File.join( File.dirname(__FILE__), 'views', 'atom.haml' )
156
+ pattern = File.join( settings.root, settings.origin, "**", "*.yaml" )
157
+ output = Dir.glob(pattern).map! do |f|
158
+ file = Filereader.new
159
+ file.filename = f
160
+ if block_given?
161
+ block.call file.get_content
162
+ end
163
+ end.compact!.sort!{|b,a| a.to_a[0] <=> b.to_a[0] }
164
+ out = '<?xml version="1.0" encoding="UTF-8"?>'+"\n"
165
+ out << haml( template, :layout => false, :locals => { :page=>get_page, :feed=>output, :author=>author } )
166
+ end
167
+
168
+
169
+ def google_ads options={}
170
+ template = File.read File.join( File.dirname(__FILE__), 'views', 'google_ads.haml' )
171
+ pattern = File.join( settings.root, settings.origin, "**", "*.yaml" )
172
+ haml( template, options.merge!( :layout => false ) )
173
+ end
174
+
175
+ def disqus options={}
176
+ template = File.read File.join( File.dirname(__FILE__), 'views', 'disqus.haml' )
177
+ haml( template, options.merge!( :layout => false ) )
178
+ end
179
+
180
+ def google_analytics options={}
181
+ template = File.read File.join( File.dirname(__FILE__), 'views', 'google_analytics.haml' )
182
+ haml( template, options.merge!( :layout => false ) )
183
+ end
184
+
185
+ def recaptcha options={}
186
+ template = File.read File.join( File.dirname(__FILE__), 'views', 'recaptcha.haml' )
187
+ haml( template, options.merge!( :layout => false ) )
188
+ end
189
+
190
+ #=== "partial" rails like partial generator
191
+ #
192
+ # This funnction accepts a string and matches it to a haml layout (with a
193
+ # underscore prepended) Sinatra's layouts directory.
194
+ #
195
+ #==== snippet
196
+ # A string that maps to a haml view in the views directory
197
+ # "partial :example, :layout => false" would map to a views/_example.haml
198
+ #
199
+ #==== options={}
200
+ # Any options you pass to this partial ger merged and sent to haml as
201
+ # sinatra's haml options (this is usefull for passing sinatra's :layout,
202
+ # :locals and other variables)
203
+ def partial(snippet, options={})
204
+ haml ('_'+snippet).to_sym, options.merge!(:layout => false)
205
+ end
206
+
207
+ #=== "link_to" rails like link_to generator
208
+ #
209
+ # This funnction accepts a 1 or 2 strings.
210
+ #
211
+ #==== text
212
+ # A string that becomes the link text. If there is no second parameter,
213
+ # link_to converts the string into a local url by replacing all spaces with
214
+ # an underscore and downcasing the string.
215
+ #
216
+ #==== url
217
+ # This string is used for 'href' in a link
218
+ def link_to(text, url="/#{text.downcase.gsub(/\s/,'_')}")
219
+ url.gsub!(/^\//, '') if url =~ /.+:\/\//
220
+ "<a href=\"#{url}\"> #{text}</a>"
221
+ end
222
+
223
+ # A simple string obuscator.
224
+ # Useful for hiding emails and such
225
+ #=== "obfuscate" simple string obuscator.
226
+ #
227
+ # This funnction accepts a 1 or 2 strings.
228
+ #
229
+ #==== str=nil
230
+ # Obfuscates a string replacing characters with html entities.
231
+ # Useful for hiding emails and such
232
+ def obfuscate(str=nil)
233
+ out = []
234
+ str.each_byte {|c| out << "&##{c};" }
235
+ out.join
236
+ end
237
+
238
+ end
239
+
240
+ helpers Soxer
241
+ end
@@ -0,0 +1,32 @@
1
+ %feed(xmlns="http://www.w3.org/2005/Atom")
2
+ %title= page['title']
3
+ %link{:href=>request.url.split(request.fullpath)[0].gsub(/:\d+/, '')}
4
+ %link(rel="self"){:href=>request.url.gsub(/:\d+/, '')}
5
+ %id= "urn:uuid:"+page['uuid']
6
+ %updated= feed[0]['mtime'].xmlschema
7
+ %author
8
+ %name= author['name']
9
+ - if author['email'] and author['email'].length > 0
10
+ %email= author['email']
11
+ - if author['uri'] and author['uri'].length > 0
12
+ %uri= author['uri']
13
+
14
+ -feed.each do |f|
15
+ %entry
16
+ %title= f['title']
17
+ %link{:href=>f['url']}
18
+ %id= "urn:uuid:"+f['uuid']
19
+ %updated= f['mtime'].xmlschema
20
+ %published= f['date'].xmlschema
21
+ - if f['summary'] and f['summary'].length > 0
22
+ - s = haml f['summary'], :layout=>false
23
+ %summary= s.gsub(%r{</?[^>]+?>}, '')
24
+ - if f['author']
25
+ %author
26
+ - if f['author']['name'] and f['author']['name'].length > 0
27
+ %name= f['author']['name']
28
+ - if f['author']['email'] and f['author']['email'].length > 0
29
+ %email= f['author']['email']
30
+ - if f['author']['uri'] and f['author']['uri'].length > 0
31
+ %uri= f['author']['uri']
32
+
@@ -0,0 +1,26 @@
1
+ - if defined?(account)
2
+ #disqus_thread
3
+ :javascript
4
+ (function() {
5
+ var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
6
+ dsq.src = 'http://#{account}.disqus.com/embed.js';
7
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
8
+ })();
9
+ %noscript Please enable JavaScript to view the #{link_to 'comments powered by Disqus', 'http://disqus.com/?ref_noscript=#{account}'}.
10
+ %a.dsq-brlink(href="http://disqus.com")
11
+ blog comments powered by
12
+ %span.logo-disqus Disqus
13
+
14
+ - if !defined?(account)
15
+ :javascript
16
+ (function() {
17
+ var links = document.getElementsByTagName('a');
18
+ var query = '?';
19
+ for(var i = 0; i < links.length; i++) {
20
+ if(links[i].href.indexOf('#disqus_thread') >= 0) {
21
+ query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&';
22
+ }
23
+ }
24
+ document.write('<script charset="utf-8" type="text/javascript" src="http://disqus.com/forums/soxer/get_num_replies.js' + query + '"></' + 'script>');
25
+ })();
26
+
@@ -0,0 +1,6 @@
1
+ :javascript
2
+ google_ad_client = "#{client}";
3
+ google_ad_slot = "#{slot}";
4
+ google_ad_width = #{width};
5
+ google_ad_height = #{height};
6
+ %script(type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js")
@@ -0,0 +1,27 @@
1
+ -#-----------------------------------------------------------------------------
2
+ -# OLD
3
+ -#-----------------------------------------------------------------------------
4
+ -#:javascript
5
+ -# var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
6
+ -# document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
7
+ -#
8
+ -#:javascript
9
+ -# try {
10
+ -# var pageTracker = _gat._getTracker("#{tracker}");
11
+ -# pageTracker._trackPageview();
12
+ -# } catch(err) {}
13
+ -#-----------------------------------------------------------------------------
14
+
15
+ -#-----------------------------------------------------------------------------
16
+ -# New, asynchronus
17
+ -#-----------------------------------------------------------------------------
18
+ :javascript
19
+ var _gaq = _gaq || [];
20
+ _gaq.push(['_setAccount', '#{tracker}']);
21
+ _gaq.push(['_trackPageview']);
22
+ (function() {
23
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
24
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
25
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
26
+ })();
27
+ -#-----------------------------------------------------------------------------
@@ -0,0 +1,19 @@
1
+ :ruby
2
+ width ||= 500;
3
+ height ||= 300;
4
+ theme ||= 'white'
5
+ lang ||= 'en'
6
+
7
+ %script(type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js")
8
+ :javascript
9
+ Recaptcha.create("#{key}", "recaptcha", {
10
+ theme: "#{theme}",
11
+ callback: Recaptcha.focus_response_field }
12
+ );
13
+ %noscript
14
+ %iframe(src="http://www.google.com/recaptcha/api/noscript?k=#{key}" height="#{height}" width="#{width}" frameborder="0")
15
+ %br
16
+ %textarea(name="recaptcha_challenge_field" rows="3" cols="40")
17
+ %input(type="hidden" name="recaptcha_response_field" value="manual_challenge")
18
+
19
+ #recaptcha
@@ -0,0 +1,10 @@
1
+ :ruby
2
+ site = env['HTTP_X_FORWARDED_HOST'] ? env['HTTP_X_FORWARDED_HOST'] : env['HTTP_HOST']
3
+
4
+ %urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
5
+ - pattern = File.join( settings.root, settings.origin, "**", "*.yaml" )
6
+ - Dir.glob(pattern).each do |file|
7
+ %url
8
+ %loc= "http://#{site}"+file.gsub(/#{settings.root}\/#{settings.origin}(.+)\.yaml$/, "\\1" ).gsub(/(.+)\/index$/, "\\1" )
9
+ %lastmod= File.mtime( file ).xmlschema
10
+
data/lib/soxer.rb ADDED
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+ require 'sinatra/base'
3
+ require 'yaml'
4
+ require 'haml'
5
+ require 'uuid'
6
+
7
+ require 'soxer/main.rb'
data/soxer.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+ s.rubygems_version = '1.3.7'
5
+
6
+ s.name = 'soxer'
7
+ s.version = '0.9.0'
8
+ s.date = '2010-10-25'
9
+ s.rubyforge_project = 'soxer'
10
+
11
+ s.summary = "Dynamic web site engine"
12
+ s.description = "Soxer is a file based dynamic web creation tool for Sinatra."
13
+
14
+ s.authors = ["Toni Anzlovar"]
15
+ s.email = 'toni@formalibre.si'
16
+ s.homepage = 'http://soxer.mutsu.org'
17
+
18
+ s.require_paths = %w[lib]
19
+
20
+ s.rdoc_options = ["--charset=UTF-8"]
21
+ # s.extra_rdoc_files = %w[README.md LICENSE]
22
+
23
+ s.add_dependency('sinatra', "~> 1")
24
+ s.add_dependency('haml', "~> 3")
25
+ s.add_dependency('uuid', "~> 2")
26
+
27
+ # = MANIFEST =
28
+ s.files = %w[
29
+ soxer.gemspec
30
+ lib/soxer.rb
31
+ lib/soxer/main.rb
32
+ lib/soxer/views/sitemap.haml
33
+ lib/soxer/views/recaptcha.haml
34
+ lib/soxer/views/google_analytics.haml
35
+ lib/soxer/views/google_ads.haml
36
+ lib/soxer/views/disqus.haml
37
+ lib/soxer/views/atom.haml
38
+ ]
39
+ # = MANIFEST =
40
+
41
+ s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
42
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: soxer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 59
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 0
10
+ version: 0.9.0
11
+ platform: ruby
12
+ authors:
13
+ - Toni Anzlovar
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-25 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: sinatra
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 1
30
+ segments:
31
+ - 1
32
+ version: "1"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: haml
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 5
44
+ segments:
45
+ - 3
46
+ version: "3"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: uuid
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ hash: 7
58
+ segments:
59
+ - 2
60
+ version: "2"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ description: Soxer is a file based dynamic web creation tool for Sinatra.
64
+ email: toni@formalibre.si
65
+ executables: []
66
+
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - soxer.gemspec
73
+ - lib/soxer.rb
74
+ - lib/soxer/main.rb
75
+ - lib/soxer/views/sitemap.haml
76
+ - lib/soxer/views/recaptcha.haml
77
+ - lib/soxer/views/google_analytics.haml
78
+ - lib/soxer/views/google_ads.haml
79
+ - lib/soxer/views/disqus.haml
80
+ - lib/soxer/views/atom.haml
81
+ has_rdoc: true
82
+ homepage: http://soxer.mutsu.org
83
+ licenses: []
84
+
85
+ post_install_message:
86
+ rdoc_options:
87
+ - --charset=UTF-8
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project: soxer
111
+ rubygems_version: 1.3.7
112
+ signing_key:
113
+ specification_version: 2
114
+ summary: Dynamic web site engine
115
+ test_files: []
116
+