soxer 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+