sinatra-hat 0.0.4

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/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