trimmer 0.0.3

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