Permalink
Browse files

[#27] Download translations with a hierarchy

The reason for this change is that most Javascript I18n plugins expect
the data in the same hierarchy that it is stored on as the locale yaml
files.

Currently, we have to come up with a conversion to store and retrieve
our keys differently to match the key to the translation. With this
change we allow the flexibility of allowing the end user (CopyCopter
client or some other javascript client) to pass a optional argument
formart with a type of hierarchy to get the data in the format of:

  {
    "en": {
      "test": {
        "one": "expected one",
        "two": "expected two"
      }
    }
  }
  • Loading branch information...
1 parent 54b7126 commit 895499aeb620d7e95dc21cd27f7acb78acfb47b2 @cmavromoustakos cmavromoustakos committed with croaky Mar 15, 2012
View
@@ -30,6 +30,7 @@ GEM
activesupport (3.1.1)
multi_json (~> 1.0)
addressable (2.2.7)
+ archive-tar-minitar (0.5.2)
arel (2.2.3)
bourbon (1.4.0)
sass (>= 3.1)
@@ -50,6 +51,7 @@ GEM
celerity (0.9.2)
childprocess (0.3.1)
ffi (~> 1.0.6)
+ columnize (0.3.6)
cucumber (1.1.9)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
@@ -61,7 +63,7 @@ GEM
nokogiri (>= 1.4.4)
rack-test (>= 0.5.7)
culerity (0.2.15)
- daemons (1.1.4)
+ daemons (1.1.8)
database_cleaner (0.7.1)
diff-lcs (1.1.3)
dynamic_form (1.1.4)
@@ -91,6 +93,8 @@ GEM
json (1.6.5)
launchy (2.0.5)
addressable (~> 2.2.6)
+ linecache19 (0.5.12)
+ ruby_core_source (>= 0.1.4)
mail (2.3.3)
i18n (>= 0.4.0)
mime-types (~> 1.16)
@@ -143,6 +147,16 @@ GEM
activesupport (>= 3.0)
railties (>= 3.0)
rspec (~> 2.8.0)
+ ruby-debug-base19 (0.11.25)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby_core_source (>= 0.1.4)
+ ruby-debug19 (0.11.6)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby-debug-base19 (>= 0.11.19)
+ ruby_core_source (0.1.5)
+ archive-tar-minitar (>= 0.5.2)
rubyzip (0.9.6.1)
sass (3.1.15)
selenium-webdriver (2.20.0)
@@ -193,6 +207,7 @@ DEPENDENCIES
pg (= 0.13.2)
rails (= 3.1.1)
rspec-rails (= 2.8.1)
+ ruby-debug19 (= 0.11.6)
shoulda-matchers (= 1.0.0)
spork (= 0.9.0)
thin (= 1.3.1)
@@ -1,7 +1,11 @@
class Api::V2::DraftBlurbsController < Api::V2::BaseController
def index
if stale? :etag => current_project.etag
- render :json => current_project.draft_json
+ if params[:format] == "hierarchy"
+ render :json => current_project.draft_json(:hierarchy => true)
+ else
+ render :json => current_project.draft_json
+ end
end
end
@@ -1,7 +1,11 @@
class Api::V2::PublishedBlurbsController < Api::V2::BaseController
def index
if stale? :etag => current_project.etag
- render :json => current_project.published_json
+ if params[:format] == "hierarchy"
+ render :json => current_project.published_json(:hierarchy => true)
+ else
+ render :json => current_project.published_json
+ end
end
end
end
View
@@ -20,17 +20,46 @@ def self.ordered
def self.to_hash(attribute)
scope = joins(:localizations => :locale).
select("blurbs.key AS blurb_key, locales.key AS locale_key, #{attribute} AS content")
- connection.select_rows(scope.to_sql).inject({}) do |result, (blurb_key, locale_key, content)|
+ blurbs = connection.select_rows(scope.to_sql)
+
+ data = blurbs.inject({}) do |result, (blurb_key, locale_key, content)|
key = [locale_key, blurb_key].join(".")
result.update key => content
end
+
+ hierarchichal_data = blurbs.inject({}) do |result, (blurb_key, locale_key, content)|
+ keys = []
+ keys = blurb_key.split('.') if blurb_key
+ result.deep_merge!({ locale_key => create_hierarchichal_hash_from_array(keys + [content]) })
+ end
+
+ { :data => data, :hierarchichal_data => hierarchichal_data }
end
def self.keys
select('key').map { |blurb| blurb.key }
end
private
+ def self.create_hierarchichal_hash_from_array(array_hierarchy, hash_hierarchy = {})
+ return hash_hierarchy if array_hierarchy.empty?
+
+ # The last 2 values in the array are the most drilled down part, so given:
+ # [d,c,b,a,1]
+ # The first Iteration you would get:
+ # { "a" => 1 }
+ # Second iteration:
+ # { "b" => { "a" => 1 } }, etc..
+ #
+ if hash_hierarchy.empty?
+ value = array_hierarchy.pop
+ hash_hierarchy.merge!(array_hierarchy.pop => value)
+ else
+ hash_hierarchy = { array_hierarchy.pop => hash_hierarchy }
+ end
+
+ return create_hierarchichal_hash_from_array(array_hierarchy, hash_hierarchy)
+ end
def update_project_caches
project.update_caches
View
@@ -51,8 +51,12 @@ def deploy!
update_caches
end
- def draft_json
- draft_cache.data
+ def draft_json(options = { :hierarchy => false })
+ if options[:hierarchy]
+ draft_cache.hierarchichal_data
+ else
+ draft_cache.data
+ end
end
def etag
@@ -71,8 +75,12 @@ def lock_key_for_creating_defaults
"project-#{id}-create-defaults"
end
- def published_json
- published_cache.data
+ def published_json(options = { :hierarchy => false })
+ if options[:hierarchy]
+ published_cache.hierarchichal_data
+ else
+ published_cache.data
+ end
end
def self.regenerate_caches
@@ -82,8 +90,8 @@ def self.regenerate_caches
end
def update_caches
- draft_cache.update_attributes! :data => generate_json(:draft_content)
- published_cache.update_attributes! :data => generate_json(:published_content)
+ draft_cache.update_attributes!(generate_json(:draft_content))
+ published_cache.update_attributes!(generate_json(:published_content))
touch
end
@@ -111,6 +119,9 @@ def generate_api_key
end
def generate_json(content)
- Yajl::Encoder.encode blurbs.to_hash(content)
+ blurbs_hash = blurbs.to_hash(content)
+ blurbs_hash[:data] = Yajl::Encoder.encode blurbs_hash[:data]
+ blurbs_hash[:hierarchichal_data] = Yajl::Encoder.encode blurbs_hash[:hierarchichal_data]
+ blurbs_hash
end
end
@@ -0,0 +1,9 @@
+class AddHierarchyTextToTextCache < ActiveRecord::Migration
+ def up
+ add_column :text_caches, :hierarchichal_data, :text
+ end
+
+ def down
+ remove_column :text_caches, :hierarchichal_data
+ end
+end
@@ -54,3 +54,41 @@ Feature: Download blurbs for a project through API
Then I should receive the following as a JSON object:
| en.test.one | update |
+ Scenario: download published blurbs with a json hieratchy for a known project
+ Given a project exists with a name of "Breakfast"
+ When I POST the v2 API URI for "Breakfast" draft blurbs:
+ | en.test.one | expected one |
+ | en.test.two | expected two |
+ And I POST the v2 API URI for "Breakfast" deploys
+ When I GET the v2 API URI for "Breakfast" published blurbs with a hierarchy param
+ Then I should receive a HTTP 200
+ And I should receive the following JSON response:
+ """
+ {
+ "en": {
+ "test": {
+ "one": "expected one",
+ "two": "expected two"
+ }
+ }
+ }
+ """
+
+ Scenario: download draft blurbs with a json hierarchy for a known project
+ Given a project exists with a name of "Breakfast"
+ When I POST the v2 API URI for "Breakfast" draft blurbs:
+ | en.test.one | expected one |
+ | en.test.two | expected two |
+ And I GET the v2 API URI for "Breakfast" draft blurbs with a hierarchy param
+ Then I should receive a HTTP 200
+ And I should receive the following JSON response:
+ """
+ {
+ "en": {
+ "test": {
+ "one": "expected one",
+ "two": "expected two"
+ }
+ }
+ }
+ """
@@ -1,11 +1,19 @@
-When /^I GET the v2 API URI for "([^"]*)" draft blurbs$/ do |project_name|
+When /^I GET the v2 API URI for "([^"]*)" draft blurbs( with a hierarchy param)?$/ do |project_name, hierarchy|
project = Project.find_by_name!(project_name)
- get_with_etag "/api/v2/projects/#{project.api_key}/draft_blurbs"
+ if hierarchy
+ get_with_etag "/api/v2/projects/#{project.api_key}/draft_blurbs?format=hierarchy"
+ else
+ get_with_etag "/api/v2/projects/#{project.api_key}/draft_blurbs"
+ end
end
-When /^I GET the v2 API URI for "([^"]*)" published blurbs$/ do |project_name|
+When /^I GET the v2 API URI for "([^"]*)" published blurbs( with a hierarchy param)?$/ do |project_name, hierarchy|
project = Project.find_by_name!(project_name)
- get_with_etag "/api/v2/projects/#{project.api_key}/published_blurbs"
+ if hierarchy
+ get_with_etag "/api/v2/projects/#{project.api_key}/published_blurbs?format=hierarchy"
+ else
+ get_with_etag "/api/v2/projects/#{project.api_key}/published_blurbs"
+ end
end
When /^I GET the v2 API URI for "([^"]*)" published blurbs twice$/ do |project_name|
@@ -46,3 +54,8 @@
actual_result = Yajl::Parser.parse(page.source)
actual_result.should == expected_hash
end
+
+Then /^I should receive the following JSON response:$/ do |string|
+ actual_result = JSON.parse(page.source)
+ actual_result.should == JSON.parse(string)
+end
View
@@ -32,25 +32,66 @@
one = Factory(:blurb, :key => 'test.one', :project => project)
two = Factory(:blurb, :key => 'test.two', :project => project)
- Factory :localization, :blurb => one, :locale => en,
- :draft_content => 'draft one', :published_content => 'published one'
- Factory :localization, :blurb => two, :locale => en,
- :draft_content => 'draft two', :published_content => 'published two'
- Factory :localization, :blurb => two, :locale => fr,
- :draft_content => 'ébauche', :published_content => 'publié'
+ Factory :localization, :blurb => one,
+ :locale => en,
+ :draft_content => 'draft one',
+ :published_content => 'published one'
+ Factory :localization, :blurb => two,
+ :locale => en,
+ :draft_content => 'draft two',
+ :published_content => 'published two'
+ Factory :localization, :blurb => two,
+ :locale => fr,
+ :draft_content => 'ébauche',
+ :published_content => 'publié'
end
it 'returns draft hash' do
- Blurb.to_hash(:draft_content).should == {
- 'en.test.one' => 'draft one', 'en.test.two' => 'draft two',
+ Blurb.to_hash(:draft_content).should include(:data => {
+ 'en.test.one' => 'draft one',
+ 'en.test.two' => 'draft two',
'fr.test.two' => 'ébauche'
- }
+ })
end
it 'returns published hash' do
- Blurb.to_hash(:published_content).should == {
- 'en.test.one' => 'published one', 'en.test.two' => 'published two',
+ Blurb.to_hash(:published_content).should include(:data => {
+ 'en.test.one' => 'published one',
+ 'en.test.two' => 'published two',
'fr.test.two' => 'publié'
- }
+ })
+ end
+
+ it 'returns a draft hash maintaining hierarchy' do
+ Blurb.to_hash(:draft_content).should include(:hierarchichal_data => {
+ 'en' => {
+ 'test' => {
+ 'one' => 'draft one',
+ 'two' => 'draft two'
+ }
+ },
+ 'fr' => {
+ 'test' => {
+ 'two' => 'ébauche'
+ }
+ }
+ })
+ end
+
+ it 'returns a published hash maintaining hierarchy' do
+ Blurb.to_hash(:published_content).should include(:hierarchichal_data => {
+ 'en' => {
+ 'test' => {
+ 'one' => 'published one',
+ 'two' => 'published two'
+ }
+ },
+ 'fr' => {
+ 'test' => {
+ 'two' => 'publié'
+ }
+ }
+ })
+
end
end
@@ -15,26 +15,25 @@ def draft_hash
it 'sets draft content for a list of blurbs' do
locale = project.locales.first
one = Factory(:blurb, :project => project, :key => 'test.one')
- Factory :localization, :blurb => one,
- :locale => locale,
- :draft_content => 'draft one',
- :published_content => 'published one'
+ Factory :localization, :blurb => one,
+ :locale => locale,
+ :draft_content => 'draft one',
+ :published_content => 'published one'
+
two = Factory :blurb, :project => project, :key => 'test.two'
- Factory :localization, :blurb => two,
- :locale => locale,
- :draft_content => 'draft two',
- :published_content => 'published two'
+ Factory :localization, :blurb => two,
+ :locale => locale,
+ :draft_content => 'draft two',
+ :published_content => 'published two'
+
create_defaults 'en.test.one' => 'new one', 'en.test.three' => 'new three'
- project.localizations(true).map(&:draft_content).
- should =~ ['draft one', 'draft two', 'new three']
- project.localizations.map(&:published_content).
- should =~ ['', 'published one', 'published two']
+ project.localizations(true).map(&:draft_content).should =~ ['draft one', 'draft two', 'new three']
+ project.localizations.map(&:published_content).should =~ ['', 'published one', 'published two']
end
it 'ignores blank keys' do
create_defaults 'en.test.one' => 'not blank', '' => 'blank'
- project.localizations(true).map(&:draft_content).
- should =~ ['not blank']
+ project.localizations(true).map(&:draft_content).should =~ ['not blank']
end
it "only updates the project once when creating several defaults" do
Oops, something went wrong.

0 comments on commit 895499a

Please sign in to comment.