trimmer 0.0.3

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in trimmer.gemspec
4
+ gemspec
data/History.md ADDED
@@ -0,0 +1,12 @@
1
+ * Version 0.0.1
2
+ * Initial commit
3
+ * Version 0.0.2
4
+ #####Changed
5
+
6
+ * Only cache templates in production environment
7
+ * Version 0.0.3
8
+ #####Changed
9
+
10
+ * Ruby 1.9 and 1.8 compatibility
11
+
12
+
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+ Trimmer Controller
2
+ =================
3
+
4
+ Rack endpoint to make templates and i18n translations available in javascript.
5
+
6
+ See specs for details.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,123 @@
1
+ require 'rack'
2
+ require 'rack/request'
3
+ require 'json'
4
+ require 'tilt'
5
+ require 'i18n'
6
+
7
+ module Trimmer
8
+
9
+ class Controller
10
+ attr_accessor :templates_path, :allowed_keys, :renderer_scope
11
+
12
+ def initialize(app, opts={})
13
+ @app = app
14
+
15
+ @templates_path = opts[:templates_path] || File.expand_path(File.dirname(__FILE__))
16
+ @allowed_keys = opts[:allowed_keys] || "*"
17
+ @renderer_scope = opts[:renderer_scope] || Object.new
18
+ end
19
+
20
+ # Handle trimmer routes
21
+ # else call the next middleware with the environment.
22
+ def call(env)
23
+ request = Rack::Request.new(env)
24
+ response = nil
25
+ case request.path
26
+ when /\/trimmer(\/([^\/]+))*\/translations\.([js]+)$/
27
+ validate_locale($2) if $2 || ($2 && $2.empty?)
28
+ response = translations($2, $3)
29
+ when /\/trimmer\/([^\/]+)\/templates\.([js]+)$/
30
+ validate_locale($1)
31
+ response = templates($1, $2)
32
+ when /\/trimmer\/([^\.|\/]+)\.([js]+)$/
33
+ validate_locale($1)
34
+ response = resources($1, $2)
35
+ else
36
+ response = @app.call(env)
37
+ end
38
+
39
+ response
40
+ end
41
+
42
+ protected
43
+
44
+ def templates(locale, ext)
45
+ [200, {'Content-Type' => 'text/javascript'}, templates_to_js(locale)]
46
+ end
47
+
48
+
49
+ def translations(locale, ext)
50
+ [200, {'Content-Type' => 'text/javascript'}, translations_to_js(:locale => locale, :only => allowed_keys)]
51
+ end
52
+
53
+ # Exports templates and translations in a single request
54
+ def resources(locale, ext)
55
+ opts = {:only => allowed_keys}
56
+ opts[:locale] = locale if locale && !locale.empty?
57
+
58
+ response = translations_to_js(opts)
59
+ response << "\n"
60
+ response << templates_to_js(locale)
61
+ [200, {'Content-Type' => 'text/javascript'}, [response]]
62
+ end
63
+
64
+ #Render a template (using Tilt)
65
+ def render_to_string(path, opts={})
66
+ if ENV['RACK_ENV'] == 'production'
67
+ template = Thread.current[:"#{path}"] || Thread.current[:"#{path}"] = Tilt.new(path, opts)
68
+ else
69
+ template = Tilt.new(path, opts)
70
+ end
71
+
72
+ ::I18n.with_exception_handler(:raise_all_exceptions) do
73
+ string = template.render(renderer_scope)
74
+ string.respond_to?(:force_encoding) ? string.force_encoding("utf-8") : string
75
+ end
76
+ end
77
+
78
+ #Validates locale is one recognised by I18n::Locale::Tag implementation
79
+ def validate_locale(locale)
80
+ raise "Invalid locale" unless ::I18n::Locale::Tag.tag(locale)
81
+ end
82
+
83
+ # Gets all templates and renders them as JSON, to be used as Mustache templates
84
+ def templates_to_js(locale)
85
+ ::I18n.with_locale(locale) do
86
+ templates = JSON.dump(get_templates_from(templates_path))
87
+ "Templates = (#{templates});"
88
+ end
89
+ end
90
+
91
+ # Traverses recursively base_path and fetches templates
92
+ def get_templates_from(base_path)
93
+ templates = {}
94
+ Dir.glob("#{base_path}/**").each do |entry|
95
+ if File.directory? entry
96
+ templates[File.basename(entry)] = get_templates_from entry
97
+ elsif File.file? entry
98
+ name = File.basename(entry).split('.').first
99
+ if !name.empty?
100
+ templates[name] = render_to_string(entry, :ugly => true)
101
+ end
102
+ end
103
+ end
104
+ templates
105
+ end
106
+
107
+ # Dumps all the translations. Options you can pass:
108
+ # :locale. If specified, will dump only translations for the given locale.
109
+ # :only. If specified, will dump only keys that match the pattern. "*.date"
110
+ def translations_to_js(options = {})
111
+ "if(typeof(I18n) == 'undefined') { I18n = {}; };\n" +
112
+ "I18n.translations = (#{locale_with_fallback(options).to_json});"
113
+ end
114
+
115
+ def locale_with_fallback options
116
+ original_translation = ::I18n.to_hash(options) || {}
117
+ fallback_translation = ::I18n.to_hash(options.merge(:locale => ::I18n.default_locale))
118
+ fallback_translation ||= {}
119
+ fallback_translation.deep_merge original_translation
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,23 @@
1
+ class Hash
2
+
3
+ def reverse_merge(other_hash)
4
+ other_hash.merge(self)
5
+ end
6
+
7
+ def reverse_merge!(other_hash)
8
+ merge!( other_hash ){|k,o,n| o }
9
+ end
10
+
11
+ def deep_merge!(other_hash)
12
+ other_hash.each_pair do |k,v|
13
+ tv = self[k]
14
+ self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
15
+ end
16
+ self
17
+ end
18
+
19
+ def deep_merge(other_hash)
20
+ dup.deep_merge!(other_hash)
21
+ end
22
+
23
+ end
@@ -0,0 +1,99 @@
1
+ require 'i18n'
2
+
3
+ module Trimmer
4
+ module I18n
5
+
6
+ def raise_all_exceptions(*args)
7
+ raise args.first
8
+ end
9
+
10
+ def with_exception_handler(tmp_exception_handler = nil)
11
+ if tmp_exception_handler
12
+ current_exception_handler = self.exception_handler
13
+ self.exception_handler = tmp_exception_handler
14
+ end
15
+ yield
16
+ ensure
17
+ self.exception_handler = current_exception_handler if tmp_exception_handler
18
+ end
19
+
20
+ def without_fallbacks
21
+ current_translate_method = ::I18n.backend.method(:translate)
22
+ ::I18n.backend.class.send(:define_method, 'translate', translate_without_fallbacks)
23
+ yield
24
+ ensure
25
+ ::I18n.backend.class.send(:remove_method, 'translate')
26
+ end
27
+
28
+ # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
29
+ MERGER = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2 }
30
+
31
+ # Exports translations from the I18n backend to a Hash
32
+ # :locale. If specified, will dump only translations for the given locale.
33
+ # :only. If specified, will dump only keys that match the pattern. "*.date"
34
+ def to_hash options = {}
35
+ options.reverse_merge!(:only => "*")
36
+
37
+ if options[:only] == "*"
38
+ data = translations
39
+ else
40
+ data = scoped_translations options[:only]
41
+ end
42
+
43
+ if options[:locale]
44
+ data[options[:locale].to_sym]
45
+ else
46
+ data
47
+ end
48
+ end
49
+
50
+ def scoped_translations(scopes) # :nodoc:
51
+ result = {}
52
+
53
+ [scopes].flatten.each do |scope|
54
+ deep_merge! result, filter(translations, scope)
55
+ end
56
+
57
+ result
58
+ end
59
+
60
+ # Filter translations according to the specified scope.
61
+ def filter(translations, scopes)
62
+ scopes = scopes.split(".") if scopes.is_a?(String)
63
+ scopes = scopes.clone
64
+ scope = scopes.shift
65
+
66
+ if scope == "*"
67
+ results = {}
68
+ translations.each do |scope, translations|
69
+ tmp = scopes.empty? ? translations : filter(translations, scopes)
70
+ results[scope.to_sym] = tmp unless tmp.nil?
71
+ end
72
+ return results
73
+ elsif translations.has_key?(scope.to_sym)
74
+ return {scope.to_sym => scopes.empty? ? translations[scope.to_sym] : filter(translations[scope.to_sym], scopes)}
75
+ end
76
+ nil
77
+ end
78
+
79
+ # Initialize and return translations
80
+ def translations
81
+ self.backend.instance_eval do
82
+ init_translations unless initialized?
83
+ translations
84
+ end
85
+ end
86
+
87
+ def deep_merge!(target, hash) # :nodoc:
88
+ target.merge!(hash, &MERGER)
89
+ end
90
+
91
+ private
92
+
93
+ def translate_without_fallbacks
94
+ method_name = RUBY_VERSION =~ /1\.9/ ? :translate : 'translate'
95
+ ancestor_module_with_translate = (::I18n.backend.class.included_modules - [::I18n::Backend::Fallbacks]).select {|m| m.instance_methods(false).include?(method_name) rescue false}
96
+ ancestor_module_with_translate.first.instance_method(method_name) rescue nil
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module Trimmer
2
+ VERSION = "0.0.3"
3
+ end
data/lib/trimmer.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'trimmer/core'
2
+ require 'trimmer/i18n'
3
+ require 'trimmer/controller'
4
+
5
+ I18n.send(:extend, Trimmer::I18n)
@@ -0,0 +1,160 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ context Trimmer::Controller do
4
+ F = ::File
5
+
6
+ def request(opts={}, &block)
7
+ Rack::MockRequest.new(Trimmer::Controller.new(@def_app,
8
+ :templates_path => (opts[:templates_dir] || templates_dir),
9
+ :allowed_keys => (opts[:allowed_keys] || nil))).send(opts[:meth]||:get, opts[:path]||@def_path, opts[:headers]||{})
10
+ end
11
+
12
+ setup do
13
+ # @def_disk_cache = F.join(F.dirname(__FILE__), 'response_cache_test_disk_cache')
14
+
15
+ @def_resources_en = <<-VALUE.strip
16
+ if(typeof(I18n) == 'undefined') { I18n = {}; };
17
+ I18n.translations = ({\"foo\":{\"trimmer\":\"trimmer\"}});
18
+ Templates = ({"foo":{"bar":"<span>trimmer</span>\\n"}});
19
+ VALUE
20
+
21
+ @def_translations_en = <<-VALUE.strip
22
+ if(typeof(I18n) == 'undefined') { I18n = {}; };
23
+ I18n.translations = ({\"foo\":{\"trimmer\":\"trimmer\"}});
24
+ VALUE
25
+
26
+ @def_templates_en = <<-VALUE.strip
27
+ Templates = ({"foo":{"bar":"<span>trimmer</span>\\n"}});
28
+ VALUE
29
+
30
+ @def_resources_es = <<-VALUE.strip
31
+ if(typeof(I18n) == 'undefined') { I18n = {}; };
32
+ I18n.translations = ({\"foo\":{\"trimmer\":\"recortadora\"}});
33
+ Templates = ({"foo":{"bar":"<span>recortadora</span>\\n"}});
34
+ VALUE
35
+
36
+ @def_translations_es = <<-VALUE.strip
37
+ if(typeof(I18n) == 'undefined') { I18n = {}; };
38
+ I18n.translations = ({\"foo\":{\"trimmer\":\"recortadora\"}});
39
+ VALUE
40
+
41
+ @def_templates_es = <<-VALUE.strip
42
+ Templates = ({"foo":{"bar":"<span>recortadora</span>\\n"}});
43
+ VALUE
44
+
45
+ @def_resources_ca = <<-VALUE.strip
46
+ if(typeof(I18n) == 'undefined') { I18n = {}; };
47
+ I18n.translations = ({\"foo\":{\"trimmer\":\"trimmer\"}});
48
+ Templates = ({"foo":{"bar":"<span>recortadora</span>\\n"}});
49
+ VALUE
50
+
51
+
52
+ @def_path = '/trimmer/en.js'
53
+ @def_value = 'hello world'
54
+ @def_app = lambda { |env| [200, {'Content-Type' => env['CT'] || 'text/html'}, @def_value]}
55
+ end
56
+
57
+ teardown do
58
+ # FileUtils.rm_rf(@def_disk_cache)
59
+ end
60
+
61
+ specify "should return a response for /trimmer/:locale.js" do
62
+ request(:path=>'/trimmer/en.js').body.should.equal(@def_resources_en)
63
+ request(:path=>'/trimmer/es.js').body.should.equal(@def_resources_es)
64
+ end
65
+
66
+ specify "should return a response for /trimmer/:locale/templates.js" do
67
+ request(:path=>'/trimmer/en/templates.js').body.should.equal(@def_templates_en)
68
+ request(:path=>'/trimmer/es/templates.js').body.should.equal(@def_templates_es)
69
+ end
70
+
71
+ specify "should return a response for /trimmer/:locale/translations.js" do
72
+ request(:path=>'/trimmer/en/translations.js').body.should.equal(@def_translations_en)
73
+ request(:path=>'/trimmer/es/translations.js').body.should.equal(@def_translations_es)
74
+ end
75
+
76
+ specify "should forward request to next middleware if it doesn't match path" do
77
+ request(:path=>'/trimmer/en/translations.json').body.should.equal(@def_value)
78
+ end
79
+
80
+ specify "should return a response for /trimmer/:locale/templates.js if fallback found" do
81
+ lambda {
82
+ request(:path=>'/trimmer/ca/templates.js').body.should.equal(@def_templates_es)
83
+ }.should.not.raise(::I18n::MissingTranslationData)
84
+ end
85
+
86
+ specify "should return a response for /trimmer/:locale.js if fallback found" do
87
+ lambda {
88
+ request(:path=>'/trimmer/ca.js').body.should.equal(@def_resources_ca)
89
+ }.should.not.raise(::I18n::MissingTranslationData)
90
+ end
91
+
92
+ specify "should reraise i18n exceptions if no fallback found when rendering all resources" do
93
+ lambda {
94
+ request(:path=>'/trimmer/ca.js', :templates_dir => templates_missing_t_dir)
95
+ }.should.raise(::I18n::MissingTranslationData)
96
+ end
97
+
98
+ specify "should reraise i18n exceptions if no fallback found when rendering templates" do
99
+ lambda {
100
+ request(:path=>'/trimmer/ca/templates.js', :templates_dir => templates_missing_t_dir)
101
+ }.should.raise(::I18n::MissingTranslationData)
102
+ end
103
+
104
+ specify "should reraise i18n exceptions if no fallbacks when rendering all resources" do
105
+ lambda {
106
+ ::I18n.without_fallbacks do
107
+ request(:path=>'/trimmer/pl.js')
108
+ end
109
+ }.should.raise(::I18n::MissingTranslationData)
110
+ end
111
+
112
+ specify "should reraise i18n exceptions if no fallback when rendering templates" do
113
+ lambda {
114
+ ::I18n.without_fallbacks do
115
+ request(:path=>'/trimmer/pl/templates.js')
116
+ end
117
+ }.should.raise(::I18n::MissingTranslationData)
118
+ end
119
+
120
+ specify "should reraise i18n exceptions if no fallbacks and locale is invalid when rendering all resources" do
121
+ lambda {
122
+ ::I18n.without_fallbacks do
123
+ request(:path=>'/trimmer/foobar.js')
124
+ end
125
+ }.should.raise(::I18n::MissingTranslationData)
126
+ end
127
+
128
+ specify "should reraise i18n exceptions if no fallbacks and locale is invalid when rendering templates" do
129
+ lambda {
130
+ ::I18n.without_fallbacks do
131
+ request(:path=>'/trimmer/foobar/templates.js')
132
+ end
133
+ }.should.raise(::I18n::MissingTranslationData)
134
+ end
135
+
136
+ specify "should filter translations if allowed_keys option set on Trimmer::Controller instance" do
137
+ request(:path=>'/trimmer/en/translations.js', :allowed_keys => "*.baz").body.should.equal("if(typeof(I18n) == 'undefined') { I18n = {}; };\nI18n.translations = ({});")
138
+ request(:path=>'/trimmer/en/translations.js', :allowed_keys => "*.foo").body.should.equal(@def_translations_en)
139
+ end
140
+
141
+ specify "should render all translations if no locale" do
142
+ request(:path=>'/trimmer/translations.js').body.should.equal(<<-RESP.strip)
143
+ if(typeof(I18n) == 'undefined') { I18n = {}; };\nI18n.translations = ({\"foo\":{\"trimmer\":\"trimmer\"},\"en\":{\"foo\":{\"trimmer\":\"trimmer\"}},\"es\":{\"foo\":{\"trimmer\":\"recortadora\"}}});
144
+ RESP
145
+ end
146
+
147
+ specify "should forward request to next middleware if no local when rendering templates" do
148
+ ::I18n.without_fallbacks do
149
+ request(:path=>'/trimmer//templates.js').body.should.equal(@def_value)
150
+ end
151
+ end
152
+
153
+ specify "should forward request to next middleware if no local when rendering resources" do
154
+ ::I18n.without_fallbacks do
155
+ request(:path=>'/trimmer.js').body.should.equal(@def_value)
156
+ end
157
+ end
158
+
159
+ end
160
+
@@ -0,0 +1,3 @@
1
+ ca:
2
+ foo:
3
+ bed: "llit"
@@ -0,0 +1,3 @@
1
+ en:
2
+ foo:
3
+ trimmer: "trimmer"
@@ -0,0 +1,3 @@
1
+ es:
2
+ foo:
3
+ trimmer: "recortadora"
@@ -0,0 +1 @@
1
+ %span=I18n.t('foo.trimmer')
@@ -0,0 +1,2 @@
1
+ %span=I18n.t('foo.trimmer')
2
+ %p=I18n.t('bar.trimmer')
@@ -0,0 +1,54 @@
1
+ require 'i18n'
2
+ require 'test/spec'
3
+ require 'rack/mock'
4
+ require File.dirname(__FILE__) + '/../lib/trimmer'
5
+
6
+ class Test::Unit::TestCase
7
+
8
+ def setup
9
+ I18n.backend = I18n::Backend::Simple.new
10
+ I18n.default_locale = :en
11
+
12
+ %w(en es).each do |locale|
13
+ I18n.load_path << [locales_dir + "/#{locale}.yml"]
14
+ end
15
+
16
+ I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
17
+ # => [:ca, :es, :en]
18
+ I18n.fallbacks.map(:ca => :es)
19
+ end
20
+
21
+ def teardown
22
+ I18n.locale = nil
23
+ I18n.default_locale = :en
24
+ I18n.load_path = []
25
+ I18n.available_locales = nil
26
+ I18n.backend = nil
27
+ end
28
+
29
+ def translations
30
+ I18n.backend.instance_variable_get(:@translations)
31
+ end
32
+
33
+ def store_translations(*args)
34
+ data = args.pop
35
+ locale = args.pop || :en
36
+ I18n.backend.store_translations(locale, data)
37
+ end
38
+
39
+ def locales_dir
40
+ File.dirname(__FILE__) + '/test_data/locales'
41
+ end
42
+
43
+ def templates_dir
44
+ File.dirname(__FILE__) + '/test_data/templates'
45
+ end
46
+
47
+ def templates_missing_t_dir
48
+ File.dirname(__FILE__) + '/test_data/templates_missing_t'
49
+ end
50
+
51
+ end
52
+
53
+
54
+
data/trimmer.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "trimmer/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "trimmer"
7
+ s.version = Trimmer::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Pablo Villalba", "Saimon Moore"]
10
+ s.email = ["pablo@teambox.com", "saimon@teambox.com"]
11
+ s.homepage = "https://github.com/teambox/trimmer"
12
+ s.summary = %q{Rack endpoint to make templates and i18n translations available in javascript}
13
+
14
+ s.rubyforge_project = "trimmer"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.add_dependency 'rack', '~> 1.1'
21
+ s.add_dependency 'i18n', '~> 0.6.0'
22
+ s.add_dependency 'tilt', '~> 1.3.3'
23
+ s.add_development_dependency 'test-spec', '~> 0.9.0'
24
+ s.add_development_dependency 'haml', '~> 3.0.25'
25
+ s.add_development_dependency 'json', '~> 1.1'
26
+ end
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trimmer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 3
10
+ version: 0.0.3
11
+ platform: ruby
12
+ authors:
13
+ - Pablo Villalba
14
+ - Saimon Moore
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-10-13 00:00:00 +02:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: rack
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 13
31
+ segments:
32
+ - 1
33
+ - 1
34
+ version: "1.1"
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: i18n
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 0
48
+ - 6
49
+ - 0
50
+ version: 0.6.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: tilt
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 29
62
+ segments:
63
+ - 1
64
+ - 3
65
+ - 3
66
+ version: 1.3.3
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: test-spec
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 59
78
+ segments:
79
+ - 0
80
+ - 9
81
+ - 0
82
+ version: 0.9.0
83
+ type: :development
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: haml
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ hash: 53
94
+ segments:
95
+ - 3
96
+ - 0
97
+ - 25
98
+ version: 3.0.25
99
+ type: :development
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: json
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ hash: 13
110
+ segments:
111
+ - 1
112
+ - 1
113
+ version: "1.1"
114
+ type: :development
115
+ version_requirements: *id006
116
+ description:
117
+ email:
118
+ - pablo@teambox.com
119
+ - saimon@teambox.com
120
+ executables: []
121
+
122
+ extensions: []
123
+
124
+ extra_rdoc_files: []
125
+
126
+ files:
127
+ - .gitignore
128
+ - Gemfile
129
+ - History.md
130
+ - README.md
131
+ - Rakefile
132
+ - lib/trimmer.rb
133
+ - lib/trimmer/controller.rb
134
+ - lib/trimmer/core.rb
135
+ - lib/trimmer/i18n.rb
136
+ - lib/trimmer/version.rb
137
+ - test/spec_trimmer_controller.rb
138
+ - test/test_data/locales/ca.yml
139
+ - test/test_data/locales/en.yml
140
+ - test/test_data/locales/es.yml
141
+ - test/test_data/templates/foo/bar.haml
142
+ - test/test_data/templates_missing_t/baz/bif.haml
143
+ - test/test_helper.rb
144
+ - trimmer.gemspec
145
+ has_rdoc: true
146
+ homepage: https://github.com/teambox/trimmer
147
+ licenses: []
148
+
149
+ post_install_message:
150
+ rdoc_options: []
151
+
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ none: false
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ hash: 3
160
+ segments:
161
+ - 0
162
+ version: "0"
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ none: false
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ hash: 3
169
+ segments:
170
+ - 0
171
+ version: "0"
172
+ requirements: []
173
+
174
+ rubyforge_project: trimmer
175
+ rubygems_version: 1.3.7
176
+ signing_key:
177
+ specification_version: 3
178
+ summary: Rack endpoint to make templates and i18n translations available in javascript
179
+ test_files:
180
+ - test/spec_trimmer_controller.rb
181
+ - test/test_data/locales/ca.yml
182
+ - test/test_data/locales/en.yml
183
+ - test/test_data/locales/es.yml
184
+ - test/test_data/templates/foo/bar.haml
185
+ - test/test_data/templates_missing_t/baz/bif.haml
186
+ - test/test_helper.rb