sinatra-hat 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Adam Strzelecki
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,27 @@
1
+ Sinatra Hat
2
+ ===========
3
+ This is a set of extensions for *Sinatra* and relative gems such as *Sequel*, *Haml*, *GetText*.
4
+
5
+ It adds following functions to your *Sinatra* project:
6
+
7
+ - Automatic translation using *GetText* or *Fast_Gettext* of *Haml* templates, and parser for `po` file generation
8
+ - Several fixes for *Sequel*, *Rack*
9
+ - Rack reloader for *Sinatra* and *GetText* translations
10
+ - String permalinks and date extensions
11
+
12
+ Setup
13
+ =====
14
+ ### 1. Install
15
+ sudo gem install nanoant-sinatra-hat -s http://gems.github.com/
16
+
17
+ Or from source:
18
+
19
+ git clone git://github.com/nanoant/sinatra-hat.git
20
+ cd sinatra-hat && rake install
21
+
22
+ Author
23
+ ======
24
+ [Adam Strzelecki](http://www.nanoant.com/)
25
+ ono@java.pl
26
+
27
+ Released under MIT license, see `MIT-LICENSE` for details.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "sinatra-hat"
8
+ gem.description = "Sinatra Ruby gem extensions for gettext, caching, reloading, etc."
9
+ gem.summary = "Sinatra Ruby gem extensions for gettext, caching, reloading, etc."
10
+ gem.email = "ono@java.pl"
11
+ gem.homepage = "http://github.com/nanoant/sinatra-hat"
12
+ gem.authors = ["Adam Strzelecki"]
13
+ gem.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*'].to_a
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION.yml')
47
+ config = YAML.load(File.read('VERSION.yml'))
48
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
49
+ else
50
+ version = ""
51
+ end
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "sinatra-hat #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
58
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
@@ -0,0 +1,36 @@
1
+ # Haml gettext module providing gettext translation for all Haml plain text
2
+ # calls
3
+ # http://pastie.org/445295
4
+
5
+ class Haml::Engine
6
+ # Inject _ gettext into plain text and tag plain text calls
7
+ def push_plain(text, options = {})
8
+ super(_(text), options)
9
+ end
10
+ def parse_tag(line)
11
+ tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
12
+ nuke_inner_whitespace, action, value, last_line = super(line)
13
+ value = _(value) unless action && action != '!' || action == '!' && value[0..0] == '=' || value.empty?
14
+ # translate inline ruby code too
15
+ value.gsub!(/_\('([^']+)'\)/) {|m| '\''+_($1)+'\''} unless action != '=' || value.empty?
16
+ attributes_hashes.each{|h| h.each{|v| v.gsub!(/_\('([^']+)'\)/){|m| '\''+_($1)+'\''} if v.is_a? String} unless h.nil? || h.empty?} unless attributes_hashes.nil? || attributes_hashes.empty?
17
+ [tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
18
+ nuke_inner_whitespace, action, value, last_line]
19
+ end
20
+ def push_flat(line)
21
+ return super(line) if @gettext_filters.nil? || !@gettext_filters.last
22
+ text = line.full.dup
23
+ text = "" unless text.gsub!(/^#{@flat_spaces}/, '')
24
+ text = _(text) if text != ''
25
+ @filter_buffer << "#{text}\n"
26
+ end
27
+ def start_filtered(name)
28
+ @gettext_filters ||= []
29
+ @gettext_filters.push( (name == 'markdown') )
30
+ super
31
+ end
32
+ def close_filtered(filter)
33
+ @gettext_filters.pop
34
+ super
35
+ end
36
+ end
@@ -0,0 +1,64 @@
1
+ # Haml gettext parser module
2
+ # http://pastie.org/445297
3
+
4
+ require 'gettext/tools/rgettext'
5
+ require 'gettext/tools/parser/ruby'
6
+ require 'haml'
7
+
8
+ class String
9
+ def escape_single_quotes
10
+ self.gsub(/'/, "\\\\'")
11
+ end
12
+ end
13
+
14
+ class Haml::Engine
15
+ # Overriden function that parses Haml tags
16
+ # Injects gettext call for plain text action.
17
+ def parse_tag(line)
18
+ tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
19
+ nuke_inner_whitespace, action, value, last_line = super(line)
20
+ @precompiled << "_('#{value.escape_single_quotes}')\n" unless action && action != '!' || action == '!' && value[0..0] == '=' || value.empty?
21
+ [tag_name, attributes, attributes_hashes, object_ref, nuke_outer_whitespace,
22
+ nuke_inner_whitespace, action, value, last_line]
23
+ end
24
+ # Overriden function that producted Haml plain text
25
+ # Injects gettext call for plain text action.
26
+ def push_plain(text, options = {})
27
+ @precompiled << "_('#{text.escape_single_quotes}')\n"
28
+ end
29
+ def push_flat(line)
30
+ return super(line) if @gettext_filters.nil? || !@gettext_filters.last
31
+ text = line.unstripped
32
+ return if text == ''
33
+ @precompiled << "_('#{text.escape_single_quotes}')\n"
34
+ end
35
+ def start_filtered(name)
36
+ @gettext_filters ||= []
37
+ @gettext_filters.push( (name == 'markdown') )
38
+ super
39
+ end
40
+ def close_filtered(filter)
41
+ @gettext_filters.pop
42
+ super
43
+ end
44
+ def flush_merged_text
45
+ # We override this method, becuase code generated here confuses GetText's Ruby parser
46
+ end
47
+ end
48
+
49
+ # Haml gettext parser
50
+ module HamlParser
51
+ module_function
52
+
53
+ def target?(file)
54
+ File.extname(file) == ".haml"
55
+ end
56
+
57
+ def parse(file, ary = [])
58
+ haml = Haml::Engine.new(IO.readlines(file).join)
59
+ code = haml.precompiled.split(/$/)
60
+ GetText::RubyParser.parse_lines(file, code, ary)
61
+ end
62
+ end
63
+
64
+ GetText::RGetText.add_parser(HamlParser)
@@ -0,0 +1,20 @@
1
+ # Temporarily fixes gettext error:
2
+ # lib/gettext/locale_path.rb:77:in `%': key not found (KeyError)
3
+
4
+ if RUBY_VERSION >= "1.9.0"
5
+
6
+ module GetText
7
+ class LocalePath
8
+ def initialize(name, topdir = nil)
9
+ @name = name
10
+ if topdir
11
+ @locale_paths = ["#{topdir}/%{lang}/LC_MESSAGES/%{name}.mo", "#{topdir}/%{lang}/%{name}.mo"]
12
+ else
13
+ @locale_paths = self.class.default_path_rules
14
+ end
15
+ @locale_paths.map! {|v| v % {:name => name, :lang => '%{lang}'} }
16
+ end
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,14 @@
1
+ # PATH_INFO and QUERY_STRING may not be defined when using lighttpd with server.error-handler-404 trick
2
+ class Rack::Handler::FastCGI
3
+ class <<self
4
+ alias rack_serve serve
5
+ def serve(request, app)
6
+ env = request.env
7
+ parts = env['REQUEST_URI'].to_s.split('?')
8
+ env['SCRIPT_NAME'] = ''
9
+ env['PATH_INFO'] = parts[0] if env['PATH_INFO'] == ''
10
+ env['QUERY_STRING'] = parts[1..-1].join('?') if parts.length > 1 && env['QUERY_STRING'] == ''
11
+ rack_serve(request, app)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ module GetText
2
+ class Reloader < ::Rack::Reloader
3
+ def initialize(app, path, cooldown=10)
4
+ @path = path
5
+ @po = Dir.glob(File.join(@path, '**', '*.po'))
6
+ @mo = Dir.glob(File.join(@path, '**', '*.mo'))
7
+ @pot = File.join(@path, 'rbigg.pot') # Used for locking
8
+ # If there are not translations, give up
9
+ cooldown = nil unless @po && @mo && @po.size > 0 && @mo.size > 0
10
+ super(app, cooldown, Stat)
11
+ end
12
+
13
+ def safe_load(file, mtime, stderr = $stderr)
14
+ @translations += 1
15
+ stderr.puts "#{self.class}: reloaded `#{FastGettext.text_domain}' translation `#{file}'"
16
+ ensure
17
+ @mtimes[file] = mtime
18
+ end
19
+
20
+ def reload!(stderr = $stderr)
21
+ @translations = 0
22
+ super
23
+ if @translations > 0
24
+ found, stat = safe_stat(@mo.first)
25
+ if found && stat
26
+ File.open(@pot, 'r') do |f|
27
+ # Ensure we don't produce translation twice
28
+ f.flock(File::LOCK_EX)
29
+ found, mstat = safe_stat(@mo.first)
30
+ if found && mstat && mstat.mtime == stat.mtime
31
+ require 'gettext/tools'
32
+ Dir.chdir @path do
33
+ GetText.create_mofiles(:po_root => '.', :mo_root => '.')
34
+ end
35
+ end
36
+ # Reload whole translation
37
+ FastGettext.add_text_domain(FastGettext.text_domain, :path => @path)
38
+ FastGettext.current_cache = {}
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ module Stat
45
+ def rotation
46
+ @po.map{|file|
47
+ found, stat = safe_stat(file)
48
+ next unless found and stat and mtime = stat.mtime
49
+ @cache[file] = found
50
+ yield(found, mtime)
51
+ }.compact
52
+ end
53
+
54
+ def safe_stat(file)
55
+ return unless file
56
+ stat = ::File.stat(file)
57
+ return file, stat if stat.file?
58
+ rescue Errno::ENOENT, Errno::ENOTDIR
59
+ @cache.delete(file) and false
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+ # Temporarily fixes Sequel Ruby 1.9 error:
2
+ # Encoding::CompatibilityError - incompatible character encodings:
3
+ # UTF-8 and ASCII-8BIT
4
+ # Since all variables are returned as ASCII-8BIT
5
+
6
+ if RUBY_VERSION >= "1.9.0"
7
+
8
+ class Sequel::Model
9
+ def self.load(row)
10
+ row.values.each{|v| v.force_encoding('utf-8') if v.is_a?(String)}
11
+ super(row)
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,153 @@
1
+ # encoding: utf-8
2
+ require 'iconv'
3
+
4
+ class Integer
5
+ def minute
6
+ self * 60
7
+ end
8
+ alias minutes minute
9
+ def hour
10
+ minute * 60
11
+ end
12
+ alias hours hour
13
+ def day
14
+ hour * 24
15
+ end
16
+ alias days day
17
+ def week
18
+ day * 7
19
+ end
20
+ alias weeks week
21
+ def month
22
+ day * 30
23
+ end
24
+ alias months month
25
+ def year
26
+ day * 365
27
+ end
28
+ alias years year
29
+ def ago
30
+ Time.now - self
31
+ end
32
+ end
33
+
34
+ class Time
35
+ def year_start
36
+ d = (self - (month-1).months - (day-1).days - hour.hours - min.minutes - sec)
37
+ d - (d.day-1).days - d.hour.hours - d.min.minutes - d.sec
38
+ end
39
+ def month_start
40
+ self - (day-1).days - hour.hours - min.minutes - sec
41
+ end
42
+ def week_start
43
+ self - ((wday + 6) % 7).days - hour.hours - min.minutes - sec
44
+ end
45
+ def midnight
46
+ self - hour.hours - min.minutes - sec
47
+ end
48
+ def noon
49
+ midnight + 12.hours
50
+ end
51
+ end
52
+
53
+ class String
54
+ def mail_utf8_subject
55
+ # 4.2 http://tools.ietf.org/html/rfc2047
56
+ '=?UTF-8?Q?'+(self.chomp.gsub(/[^ !-<>-^`-~]/){|m| m.unpack('C*').map{|c| '=%02X' % c}.join}.gsub(/\s/, '_'))+'?='
57
+ end
58
+ def cdata
59
+ "<![CDATA[#{self.gsub(/\]\]>/,']]]]><![CDATA[>')}]]>"
60
+ end
61
+ def md5
62
+ hash = Digest::MD5.new
63
+ hash << self
64
+ hash.hexdigest
65
+ end
66
+ def sha1
67
+ hash = Digest::SHA1.new
68
+ hash << self
69
+ hash.hexdigest
70
+ end
71
+ # This is Pligg style password hash
72
+ def pwdhash(salt=nil)
73
+ salt = String.random_password.md5 if salt.nil?
74
+ salt = salt[0..8]
75
+ salt+(salt+self).sha1
76
+ end
77
+ def excerpt(chars=nil)
78
+ first = split(/(?:\n\r?){2,}/)[0] || ""
79
+ return first if chars.nil?
80
+ words = first.split(' ')
81
+ pos, count = words.inject([0, 0]) do |c, v|
82
+ c[1] + v.length < chars ? [c[0] + 1, c[1] + v.length] : c
83
+ end
84
+ words[0..pos].join(' ') + ((pos == words.size) ? '' : '…')
85
+ end
86
+ def utf8_length
87
+ unpack('U*').length
88
+ end
89
+ def pad(other, extra=0)
90
+ padding = other.utf8_length - utf8_length
91
+ padding = 0 if padding < 0
92
+ ' ' * (padding+extra) + self
93
+ end
94
+ def self.random_password(length=9, strength=0)
95
+ vowels = 'aeuy'
96
+ consonants = 'bdghjmnpqrstvz'
97
+ consonants += 'BDGHJLMNPQRSTVWXZ' if strength & 1 != 0
98
+ vowels += 'AEUY' if strength & 2 != 0
99
+ consonants += '23456789' if strength & 4 != 0
100
+ consonants += '@#$%' if strength & 8 != 0
101
+ password = '';
102
+ alt = rand(2)
103
+ length.times do
104
+ password += consonants[rand(consonants.size - 1)].chr if alt != 0
105
+ password += vowels[rand(vowels.size - 1)].chr if alt == 0
106
+ alt = 1 - alt
107
+ end
108
+ password
109
+ end
110
+ def comma_split
111
+ CSV.parse_line(gsub(/"\s+,/,'",').gsub(/,\s+"/,',"')).collect{|t| t.strip unless t.nil?}.delete_if{|t| t.nil? || t.empty?}
112
+ end
113
+ # http://gist.github.com/93045
114
+ def to_permalink
115
+ Iconv.iconv('ascii//translit//IGNORE', 'utf-8', self).first.gsub("'", "").gsub(/[^\x00-\x7F]+/, '').gsub(/[^a-zA-Z0-9-]+/, '-').gsub(/--+/, '-').gsub(/^-/, '').gsub(/-$/, '').downcase
116
+ end
117
+ end
118
+
119
+ class Array
120
+ def comma_join
121
+ map{|v| s = v.gsub!(/,/,',') || v.gsub!(/"/,'""'); s ? '"'+v+'"' : v}.join(', ')
122
+ end
123
+ end
124
+
125
+ if defined? Rack
126
+ class Rack::Request
127
+ def url(path=nil)
128
+ url = scheme + "://"
129
+ url << host
130
+ if scheme == "https" && port != 443 ||
131
+ scheme == "http" && port != 80
132
+ url << ":#{port}"
133
+ end
134
+ url << (path ? path : fullpath)
135
+ url
136
+ end
137
+ end
138
+ end
139
+
140
+ def silence_warnings
141
+ old_verbose, $VERBOSE = $VERBOSE, nil
142
+ yield
143
+ ensure
144
+ $VERBOSE = old_verbose
145
+ end
146
+
147
+ if RUBY_VERSION < "1.9.0"
148
+ class Symbol
149
+ def to_proc
150
+ proc { |obj, *args| obj.send(self, *args) }
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,11 @@
1
+ # Reload scripts and reset routes on change
2
+ class Sinatra::Reloader < Rack::Reloader
3
+ def safe_load(file, mtime, stderr = $stderr)
4
+ if file == ::Sinatra::Application.app_file
5
+ ::Sinatra::Application.reset!
6
+ ::Sinatra::Application.clear_cache! if ::Sinatra::Application.respond_to? :clear_cache!
7
+ stderr.puts "#{self.class}: reseting routes and cache"
8
+ end
9
+ super
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ # Sinatra 0.9.x Haml template cache module
2
+
3
+ # Caches Haml templates in Class instance "render_haml_#{template}" methods,
4
+ # avoiding reparsing and recompiling of the template on every page reload.
5
+
6
+ class Sinatra::Base
7
+ # Override function responsible for calling internal Haml rendering routines
8
+ def haml(template, options={}, locals={})
9
+ method = "render_haml_#{template}".to_sym
10
+ return super(template, options, locals) unless self.respond_to? method
11
+ locals = options.delete(:locals) || locals || {}
12
+ if options[:layout] != false
13
+ __send__(:render_haml_layout, locals) { __send__(method, locals) }
14
+ else
15
+ __send__(method, locals)
16
+ end
17
+ end
18
+ def render_haml(template, data, options, locals, &block)
19
+ method = "render_haml_#{template}".to_sym
20
+ ::Haml::Engine.new(data, options).def_method(self.class, method, *(locals.keys))
21
+ __send__(method, locals, &block)
22
+ end
23
+ def self.clear_cache!
24
+ instance_methods.grep(/^render_haml_/).each{|m| remove_method m}
25
+ end
26
+ def clear_cache!; self.class.clear_cache! end
27
+ end
@@ -0,0 +1,6 @@
1
+ require 'test_helper'
2
+
3
+ class SinatraHatTest < Test::Unit::TestCase
4
+ def test_empty
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'sinatra-hat'
7
+
8
+ class Test::Unit::TestCase
9
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-hat
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Adam Strzelecki
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-02-21 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Sinatra Ruby gem extensions for gettext, caching, reloading, etc.
23
+ email: ono@java.pl
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.markdown
30
+ files:
31
+ - MIT-LICENSE
32
+ - README.markdown
33
+ - Rakefile
34
+ - VERSION
35
+ - lib/gettext/haml.rb
36
+ - lib/gettext/haml_parser.rb
37
+ - lib/gettext/ruby19_fix.rb
38
+ - lib/rack/fastcgi_fix.rb
39
+ - lib/rack/gettext_reloader.rb
40
+ - lib/sequel/utf8_fix.rb
41
+ - lib/sinatra-hat.rb
42
+ - lib/sinatra/reloader.rb
43
+ - lib/sinatra/template_cache.rb
44
+ - test/sinatra-hat_test.rb
45
+ - test/test_helper.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/nanoant/sinatra-hat
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.5.2
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Sinatra Ruby gem extensions for gettext, caching, reloading, etc.
80
+ test_files:
81
+ - test/sinatra-hat_test.rb
82
+ - test/test_helper.rb