diff --git a/.gitignore b/.gitignore index 3ddd6a1..2f516e9 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ public/uploads/**/* .sass-cache/ uploads/* config/developers/* + +/.idea/ diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 0000000..36ff528 --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +translatable diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..2aaf252 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-1.9.3-p392 diff --git a/.rvmrc b/.rvmrc deleted file mode 100644 index 3f7b2ac..0000000 --- a/.rvmrc +++ /dev/null @@ -1 +0,0 @@ -rvm use --create ree-1.8.7-2011.12@translatable diff --git a/Gemfile b/Gemfile index b812690..1719f4f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,45 +1,41 @@ source 'http://rubygems.org' -gem 'rails', '3.1.1' +gem 'rails', '~> 3.2.17' -gem 'haml', '~> 3.1.3' +group :assets do + gem 'sass-rails', '~> 3.2.3' + gem 'coffee-rails', '~> 3.2.1' + gem 'uglifier', '>= 1.0.3', :require => 'uglifier' +end + +gem 'haml', '~> 4.0.5' gem 'settingslogic', '~> 2.0.6' gem 'redis', '~> 2.2.2' gem 'redis-store', '~> 1.0.0.rc1' -gem "SystemTimer", "~> 1.2" -gem 'thin', '~> 1.2.11' gem 'unicorn', '~> 4.1.1' -gem 'eycap', ">= 0.5.22" - -gem 'therubyracer', '~> 0.9.4', :groups => [:staging, :production] - -# Gems used only for assets and not required -# in production environments by default. -group :assets do - gem 'sass-rails', "~> 3.1.4" - gem 'coffee-rails', "~> 3.1.1" - gem 'uglifier', ">= 1.0.3" -end +gem 'redis_reload', git: 'git@github.com:g5search/redis_reload.git' -gem 'jquery-rails', "~> 1.0.14" +gem 'therubyracer', :groups => [:staging, :production] group :development do - gem 'capistrano', "~> 2.8.0" + gem "capistrano", "~> 2.14.1" + gem "capistrano-maintenance", :require => false + gem "eycap", " ~> 0.6.11", :require => false end group :test do gem 'turn', :require => false - gem 'rspec-rails', '~> 2.6.1' - gem 'guard-rspec', '>= 0.4.3' - gem 'guard-spork', '>= 0.2.1' - gem 'capybara-webkit', '>= 0.6.1' + gem 'rspec-rails' + gem 'capybara' + gem 'poltergeist' + gem 'fakeredis' end unless ENV["TRAVIS"] - #gem 'ruby-debug19', :require => 'ruby-debug', :group => [:developmen, :test] + #gem 'ruby-debug19', :require => 'ruby-debug', :group => [:development, :test] group :test do - gem 'rb-fsevent', '>= 0.4.3' + gem 'rb-fsevent', '0.9.2' gem 'growl', '>= 1.0.3' gem "spork", '>= 0.9.0.rc9' end diff --git a/Gemfile.lock b/Gemfile.lock index 79e8ebc..010ce10 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,220 +1,204 @@ +GIT + remote: git@github.com:g5search/redis_reload.git + revision: 4ac3b2fd2ee5d8600afca094df3371c503a3bfbb + specs: + redis_reload (0.0.1) + redis + redis-rdb + GEM remote: http://rubygems.org/ specs: - SystemTimer (1.2.3) - actionmailer (3.1.1) - actionpack (= 3.1.1) - mail (~> 2.3.0) - actionpack (3.1.1) - activemodel (= 3.1.1) - activesupport (= 3.1.1) + actionmailer (3.2.17) + actionpack (= 3.2.17) + mail (~> 2.5.4) + actionpack (3.2.17) + activemodel (= 3.2.17) + activesupport (= 3.2.17) builder (~> 3.0.0) erubis (~> 2.7.0) - i18n (~> 0.6) - rack (~> 1.3.2) - rack-cache (~> 1.1) - rack-mount (~> 0.8.2) + journey (~> 1.0.4) + rack (~> 1.4.5) + rack-cache (~> 1.2) rack-test (~> 0.6.1) - sprockets (~> 2.0.2) - activemodel (3.1.1) - activesupport (= 3.1.1) + sprockets (~> 2.2.1) + activemodel (3.2.17) + activesupport (= 3.2.17) builder (~> 3.0.0) - i18n (~> 0.6) - activerecord (3.1.1) - activemodel (= 3.1.1) - activesupport (= 3.1.1) - arel (~> 2.2.1) + activerecord (3.2.17) + activemodel (= 3.2.17) + activesupport (= 3.2.17) + arel (~> 3.0.2) tzinfo (~> 0.3.29) - activeresource (3.1.1) - activemodel (= 3.1.1) - activesupport (= 3.1.1) - activesupport (3.1.1) + activeresource (3.2.17) + activemodel (= 3.2.17) + activesupport (= 3.2.17) + activesupport (3.2.17) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - ansi (1.3.0) - arel (2.2.1) - builder (3.0.0) - capistrano (2.8.0) + ansi (1.4.3) + arel (3.0.3) + builder (3.0.4) + capistrano (2.14.2) highline net-scp (>= 1.0.0) net-sftp (>= 2.0.0) net-ssh (>= 2.0.14) net-ssh-gateway (>= 1.1.0) - capybara (1.1.1) + capistrano-maintenance (0.0.4) + capistrano (>= 2.0.0) + capybara (2.2.1) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) - selenium-webdriver (~> 2.0) - xpath (~> 0.1.4) - capybara-webkit (0.7.2) - capybara (>= 1.0.0, < 1.2) - childprocess (0.2.2) - ffi (~> 1.0.6) - coffee-rails (3.1.1) + xpath (~> 2.0) + cliver (0.3.2) + coffee-rails (3.2.2) coffee-script (>= 2.2.0) - railties (~> 3.1.0) + railties (~> 3.2.0) coffee-script (2.2.0) coffee-script-source execjs - coffee-script-source (1.1.2) - daemons (1.1.4) - diff-lcs (1.1.3) + coffee-script-source (1.7.0) + diff-lcs (1.2.4) erubis (2.7.0) - eventmachine (0.12.10) - execjs (1.2.9) - multi_json (~> 1.0) - eycap (0.5.22) + execjs (2.0.2) + eycap (0.6.11) capistrano (>= 2.2.0) - hoe (>= 1.5.1) - ffi (1.0.9) + fakeredis (0.3.3) + redis (~> 2.2.0) growl (1.0.3) - guard (0.8.4) - thor (~> 0.14.6) - guard-rspec (0.5.0) - guard (>= 0.8.4) - guard-spork (0.3.1) - guard (>= 0.8.4) - spork (>= 0.8.4) - haml (3.1.3) - highline (1.6.2) - hike (1.2.1) - hoe (3.0.5) - rake (~> 0.8) - i18n (0.6.0) - jquery-rails (1.0.14) - railties (~> 3.0) - thor (~> 0.14) - json (1.6.1) - json_pure (1.6.1) - kgio (2.6.0) - libv8 (3.3.10.2) - mail (2.3.0) - i18n (>= 0.4.0) + haml (4.0.5) + tilt + highline (1.6.21) + hike (1.2.3) + i18n (0.6.9) + journey (1.0.4) + json (1.8.1) + kgio (2.8.0) + libv8 (3.11.8.17) + mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.16) - multi_json (1.0.3) - net-scp (1.0.4) - net-ssh (>= 1.99.1) - net-sftp (2.0.5) - net-ssh (>= 2.0.9) - net-ssh (2.2.1) - net-ssh-gateway (1.1.0) - net-ssh (>= 1.99.1) - nokogiri (1.5.0) - polyglot (0.3.2) - rack (1.3.4) - rack-cache (1.1) + mime-types (1.25.1) + mini_portile (0.5.2) + multi_json (1.9.0) + net-scp (1.1.2) + net-ssh (>= 2.6.5) + net-sftp (2.1.2) + net-ssh (>= 2.6.5) + net-ssh (2.8.0) + net-ssh-gateway (1.2.0) + net-ssh (>= 2.6.5) + nokogiri (1.6.1) + mini_portile (~> 0.5.0) + poltergeist (1.5.0) + capybara (~> 2.1) + cliver (~> 0.3.1) + multi_json (~> 1.0) + websocket-driver (>= 0.2.0) + polyglot (0.3.4) + rack (1.4.5) + rack-cache (1.2) rack (>= 0.4) - rack-mount (0.8.3) - rack (>= 1.0.0) - rack-ssl (1.3.2) + rack-ssl (1.3.3) rack - rack-test (0.6.1) + rack-test (0.6.2) rack (>= 1.0) - rails (3.1.1) - actionmailer (= 3.1.1) - actionpack (= 3.1.1) - activerecord (= 3.1.1) - activeresource (= 3.1.1) - activesupport (= 3.1.1) + rails (3.2.17) + actionmailer (= 3.2.17) + actionpack (= 3.2.17) + activerecord (= 3.2.17) + activeresource (= 3.2.17) + activesupport (= 3.2.17) bundler (~> 1.0) - railties (= 3.1.1) - railties (3.1.1) - actionpack (= 3.1.1) - activesupport (= 3.1.1) + railties (= 3.2.17) + railties (3.2.17) + actionpack (= 3.2.17) + activesupport (= 3.2.17) rack-ssl (~> 1.3.2) rake (>= 0.8.7) rdoc (~> 3.4) - thor (~> 0.14.6) - raindrops (0.7.0) - rake (0.9.2) - rb-fsevent (0.4.3.1) - rdoc (3.10) + thor (>= 0.14.6, < 2.0) + raindrops (0.11.0) + rake (10.1.1) + rb-fsevent (0.9.2) + rdoc (3.12.2) json (~> 1.4) redis (2.2.2) + redis-rdb (0.1.0) redis-store (1.0.0.1) redis (~> 2.2.1) - rspec (2.6.0) - rspec-core (~> 2.6.0) - rspec-expectations (~> 2.6.0) - rspec-mocks (~> 2.6.0) - rspec-core (2.6.4) - rspec-expectations (2.6.0) - diff-lcs (~> 1.1.2) - rspec-mocks (2.6.0) - rspec-rails (2.6.1) - actionpack (~> 3.0) - activesupport (~> 3.0) - railties (~> 3.0) - rspec (~> 2.6.0) - rubyzip (0.9.4) - sass (3.1.10) - sass-rails (3.1.4) - actionpack (~> 3.1.0) - railties (~> 3.1.0) - sass (>= 3.1.4) - sprockets (~> 2.0.0) - tilt (~> 1.3.2) - selenium-webdriver (2.8.0) - childprocess (>= 0.2.1) - ffi (>= 1.0.7) - json_pure - rubyzip - settingslogic (2.0.6) - spork (0.9.0.rc9) - sprockets (2.0.2) + ref (1.0.5) + rspec-core (2.14.2) + rspec-expectations (2.14.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.1) + rspec-rails (2.14.0) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + sass (3.2.14) + sass-rails (3.2.6) + railties (~> 3.2.0) + sass (>= 3.1.10) + tilt (~> 1.3) + settingslogic (2.0.9) + spork (1.0.0rc3) + sprockets (2.2.2) hike (~> 1.2) + multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - therubyracer (0.9.8) - libv8 (~> 3.3.10) - thin (1.2.11) - daemons (>= 1.0.9) - eventmachine (>= 0.12.6) - rack (>= 1.0.0) - thor (0.14.6) - tilt (1.3.3) - treetop (1.4.10) + therubyracer (0.11.4) + libv8 (~> 3.11.8.12) + ref + thor (0.18.1) + tilt (1.4.1) + treetop (1.4.15) polyglot polyglot (>= 0.3.1) - turn (0.8.2) - ansi (>= 1.2.2) - tzinfo (0.3.30) - uglifier (1.0.3) + turn (0.9.6) + ansi + tzinfo (0.3.38) + uglifier (2.4.0) execjs (>= 0.3.0) - multi_json (>= 1.0.2) + json (>= 1.8.0) unicorn (4.1.1) kgio (~> 2.4) rack raindrops (~> 0.6) - xpath (0.1.4) + websocket-driver (0.3.2) + xpath (2.0.0) nokogiri (~> 1.3) PLATFORMS ruby DEPENDENCIES - SystemTimer (~> 1.2) - capistrano (~> 2.8.0) - capybara-webkit (>= 0.6.1) - coffee-rails (~> 3.1.1) - eycap (>= 0.5.22) + capistrano (~> 2.14.1) + capistrano-maintenance + capybara + coffee-rails (~> 3.2.1) + eycap (~> 0.6.11) + fakeredis growl (>= 1.0.3) - guard-rspec (>= 0.4.3) - guard-spork (>= 0.2.1) - haml (~> 3.1.3) - jquery-rails (~> 1.0.14) - rails (= 3.1.1) - rb-fsevent (>= 0.4.3) + haml (~> 4.0.5) + poltergeist + rails (~> 3.2.17) + rb-fsevent (= 0.9.2) redis (~> 2.2.2) redis-store (~> 1.0.0.rc1) - rspec-rails (~> 2.6.1) - sass-rails (~> 3.1.4) + redis_reload! + rspec-rails + sass-rails (~> 3.2.3) settingslogic (~> 2.0.6) spork (>= 0.9.0.rc9) - therubyracer (~> 0.9.4) - thin (~> 1.2.11) + therubyracer turn uglifier (>= 1.0.3) unicorn (~> 4.1.1) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 9b71e63..cc50d20 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,7 +1,4 @@ // - //= require jquery - //= require jquery-ui - //= require jquery_ujs //= require ../../../vendor/assets/javascripts/externals //= require_tree . diff --git a/app/controllers/translations_controller.rb b/app/controllers/translations_controller.rb index 5c1a9cf..5034639 100644 --- a/app/controllers/translations_controller.rb +++ b/app/controllers/translations_controller.rb @@ -1,8 +1,13 @@ class TranslationsController < ApplicationController respond_to :html, :json + before_filter :tidy_params, :only => [:create, :update] before_filter :load_translation, :only => [:update, :destroy] + def tidy_params + tidy_hash(params) + end + def index @translation ||= Translation.new(:locale => locale) end @@ -14,7 +19,8 @@ def create flash[:notice] = "Successfully created I18N Translation." redirect_to translations_url else - render :action => :index + flash[:error] = "Error saving invalid translation." + render :action => :index, :status => 500 end end @@ -47,4 +53,16 @@ def load_translation @translation = Translation.find(:locale => locale, :key => params[:key]) @translation.value = params[:value] if @translation && params[:value] end + + def tidy_hash(hash) + hash.each_pair do |k,v| + if v.respond_to?(:each_pair) + hash[k] = tidy_hash(v) + else + hash[k] = ActiveSupport::Multibyte::Unicode.tidy_bytes(v) + end + end + + hash + end end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index f4f2358..09552c0 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -2,6 +2,7 @@ %html %head %title translatable + = javascript_include_tag "http://code.jquery.com/jquery-1.5.2.min.js" = stylesheet_link_tag "application" = javascript_include_tag "application" = csrf_meta_tags diff --git a/app/views/translations/index.html.haml b/app/views/translations/index.html.haml index b15e638..6c81027 100644 --- a/app/views/translations/index.html.haml +++ b/app/views/translations/index.html.haml @@ -34,7 +34,7 @@ - for translation in translations %tr{'data-key' => translation.key, 'data-url' => translation_url('x')} %td - = link_to(image_tag('/assets/trash_can1.png'), '#', :class => 'destroy-translation') + = link_to(image_tag(asset_path("trash_can1.png")), '#', :class => 'destroy-translation') %td.key= translation.key %td.value %span.editable= translation.value diff --git a/config/deploy.rb b/config/deploy.rb index f630c8c..c653f90 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -1,9 +1,13 @@ # For complete deployment instructions, see the following support guide: # http://www.engineyard.com/support/guides/deploying_your_application_with_capistrano -require "eycap/recipes" +require "eycap" +require "capistrano/maintenance" require "bundler/capistrano" +parent_dir = File.expand_path(File.join(File.dirname(__FILE__), "..")) +require "#{parent_dir}/config/developers/logins" if File.exist?("#{parent_dir}/config/developers/logins.rb") + # Servers DEMO = "72.46.233.145:7000" STAGING = "72.46.233.146:7000" @@ -21,10 +25,10 @@ # the URL of the repository you want this recipe to correspond to. The :deploy_to variable must be # the root of the application. -set :keep_releases, 5 +set :keep_releases, 3 set :application, "translatable" set :user, "g5search" -set :password, ENV['EY_PASSWORD'] +set :password, EY_PASSWORD set :deploy_to, "/data/#{application}" set :monit_group, "translatable" set :runner, "g5search" @@ -81,6 +85,12 @@ set :rails_env, "staging" end +task :demo do + role :web, DEMO + role :app, DEMO, :mongrel => true + set :rails_env, "demo" +end + #after "deploy:update_code", "pipeline_precompile" # Do not change below unless you know what you are doing! diff --git a/lib/redis_restore.rb b/lib/redis_restore.rb new file mode 100644 index 0000000..15a174c --- /dev/null +++ b/lib/redis_restore.rb @@ -0,0 +1,22 @@ +class RedisRestore + attr_accessor :src_rdb, :src_db + + def initialize(atts={}) + self.src_rdb = atts[:src_rdb] || raise('src_rdb is required') + self.src_db = atts[:src_db] || raise('src_db is required') + end + + def restore + RedisReload::Reloader.new(reloader_options).reload + end + + def reloader_options + { + rdb_filename: self.src_rdb, + source_db: self.src_db, + redis_host: Settings.redis_host, + redis_port: Settings.redis_port, + target_db: Settings.redis_db + } + end +end \ No newline at end of file diff --git a/lib/tasks/restore.rake b/lib/tasks/restore.rake new file mode 100644 index 0000000..15e2f4e --- /dev/null +++ b/lib/tasks/restore.rake @@ -0,0 +1,15 @@ +require 'rake' + +namespace :translatable do + # SRC_RDB=/usr/local/var/db/redis/dump.rdb + # SRC_DB=6 + desc 'restore Redis from rdb file' + task restore: :environment do + redis_restore = RedisRestore.new(src_rdb: ENV['SRC_RDB'], src_db: ENV['SRC_DB']) + + puts "restoring with #{redis_restore.reloader_options}" + redis_restore.restore + + puts "finished!" + end +end \ No newline at end of file diff --git a/spec/controllers/translations_controller_spec.rb b/spec/controllers/translations_controller_spec.rb index 5841d49..33b685c 100644 --- a/spec/controllers/translations_controller_spec.rb +++ b/spec/controllers/translations_controller_spec.rb @@ -35,21 +35,80 @@ end describe "post create" do - before(:each) do - Translation.send(:store).flushdb + subject(:create) do + xhr :post, :create, translation: {key: key, value: value, locale: locale} + end + + before(:each) { Translation.send(:store).flushdb } + + let(:key) { 'create.key' } + let(:value) { 'value for you' } + let(:locale) { 'ck-KK' } + + context 'when translation is valid ASCII' do + it 'should redirect to the translations index' do + create + response.should redirect_to(translations_url) + end + + it 'should create the translation' do + create + expect(Translation.locale_value(locale, key)).to eq(value) + end + end + + context 'when translation key is invalid' do + let(:key) { '' } + + it 'should return 500' do + create + expect(response.status).to eq(500) + end + + it 'should render the translation index' do + create + expect(response).to render_template(:index) + end + + it 'should display an error message' do + create + expect(flash[:error]).to eq('Error saving invalid translation.') + end + + it 'should not save the translation' do + create + expect(Translation.locale_value(locale, key)).to_not be + end end - it "should create translation if valid" do - xhr :post, :create, :translation => {:key => 'create.key', :value => 'value for you', :locale => 'ck-KK'} + context 'when translation key is not UTF-8' do + let(:key) { "\x91key\x92".force_encoding('cp1252') } + + it 'should redirect to the translations index' do + create + response.should redirect_to(translations_url) + end - response.should redirect_to(translations_url) - Translation.locale_value('ck-KK', 'create.key').should == 'value for you' + it 'should create the translation with a UTF-8 string as the key' do + create + utf8_encoded_key = "\u2018key\u2019".force_encoding('utf-8') + expect(Translation.locale_value(locale, utf8_encoded_key)).to eq(value) + end end - it "should return 500 and not save if not valid" do - xhr :post, :create, :translation => {:key => '', :value => 'value for you', :locale => 'ck-KK'} + context 'when translation value is not UTF-8' do + let(:value) { "testing \x97 value".force_encoding('cp1252') } + + it 'should redirect to the translations index' do + create + response.should redirect_to(translations_url) + end - response.should render_template :index + it 'should create the translation with a UTF-8 string as the value' do + create + utf8_encoded_value = "testing \u2014 value".force_encoding('utf-8') + expect(Translation.locale_value(locale, key)).to eq(utf8_encoded_value) + end end end @@ -68,23 +127,63 @@ end describe "put update" do + subject(:update) do + xhr :put, :update, id: translation_id, key: key, value: value + end + before(:each) do Translation.send(:store).flushdb - Translation.new(:locale => 'en-US', :key => 'error', :value => 'big error!').save - Translation.locale_value('en-US', 'error').should == 'big error!' + Translation.new(locale: locale, key: key, value: orig_value).save + end + + let(:locale) { 'en-US' } + let(:translation_id) { 'yada' } + let(:key) { 'error' } + let(:orig_value) { 'big error!' } + + context 'when value is valid ASCII' do + let(:value) { 'even bigger' } + + it 'should respond successfully' do + update + expect(response).to be_success + end + + it 'should update the stored translation value' do + update + expect(Translation.locale_value(locale, key)).to eq(value) + end end - it "should update key" do - xhr :put, :update, :id => 'yada', :key => 'error', :value => 'even bigger' + context 'when translation does not exist' do + let(:translation_id) { 'xx' } + let(:key) { '' } + let(:value) { 'should never happen' } + + it 'should return 500' do + update + expect(response.status).to eq(500) + end - response.should be_success - Translation.locale_value('en-US', 'error').should == 'even bigger' + it 'should not save translation' do + update + expect(Translation.locale_value(locale, key)).to be_nil + end end - it "should return 500 and not save if key not found" do - xhr :put, :update, :id => 'xx', :key => '', :value => 'value for you', :locale => 'ck-KK' + context 'when new value is not UTF-8' do + let(:value) { "testing \x97 value".force_encoding('binary') } + + it 'should respond successfully' do + update + expect(response).to be_success + end - response.status.should == 500 + it 'should update the stored translation value' do + utf8_encoded_value = "testing \u2014 value".force_encoding('utf-8') + update + expect(Translation.locale_value(locale, key)).to eq(utf8_encoded_value) + end end end end diff --git a/spec/features/translations_spec.rb b/spec/features/translations_spec.rb new file mode 100644 index 0000000..59543e7 --- /dev/null +++ b/spec/features/translations_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper' + +describe "Translations" do + + before(:each) do + Translation.send(:store).flushdb + end + + describe "GET /" do + before(:each) do + visit translations_path + end + + it "displays i18n locale filtering" do + page.should have_content("Locales") + page.should have_css("input#filter_by[type=text]") + end + + it "displays create key form" do + within(:css, 'form.new_translation') { + page.should have_css("label[for=translation_key]") + page.should have_css("label[for=translation_value]") + page.should have_css("input[type=submit]") + } + end + + it "displays key/value grid" do + page.should have_content("I18n Key") + page.should have_content("Value") + end + end + + describe "update translation" do + def update_value(value) + within(:css, "tr[data-key=#{@key}]") { + find('span.editable').click + find('input[type=text]').set(value) + find('button.save').click + } + end + + before(:each) do + @key = "foo" + @locale = 'en-US' + Translation.new(:locale => @locale, :key => @key, :value => 'bar').save + visit translations_path(:locale => @locale) + page.should have_css("tr[data-key=#{@key}]") + end + + it "updates the translation's value", :js => true do + update_value('baz') + visit translations_path(:locale => @locale) + find("tbody tr[data-key=#{@key}] td.value span.editable").should have_text("baz") + end + + it "replaces invalid encoded characters", :js => true do + windows_encoded_value = "\x93quoted \x97 value\x94".force_encoding('cp1252') + utf8_encoded_value = "\u201cquoted \u2014 value\u201d".force_encoding('utf-8') + update_value(windows_encoded_value) + visit translations_path(:locale => @locale) + find("tbody tr[data-key=#{@key}] td.value span.editable").should have_text(utf8_encoded_value) + end + end + + describe "create translation" do + def create_translation(key, value) + visit translations_path + fill_in 'Key', :with => key + fill_in 'Value', :with => value + click_button 'Create' + end + + it "should create valid translation" do + create_translation('trankey', 'tranvalue') + + find_field('Key').value.should be_blank + find_field('Value').value.should be_blank + + find("tbody tr td.key").should have_text("trankey") + find("tbody tr td.value span.editable").should have_text("tranvalue") + end + + it "should replace invalid encoded characters" do + windows_encoded_key = "\x91key\x92".force_encoding('cp1252') + utf8_encoded_key = "\u2018key\u2019".force_encoding('utf-8') + + windows_encoded_value = "testing \x97 value".force_encoding('cp1252') + utf8_encoded_value = "testing \u2014 value".force_encoding('utf-8') + create_translation(windows_encoded_key, windows_encoded_value) + + find("tbody tr td.key").should have_text(utf8_encoded_key) + find("tbody tr td.value span.editable").should have_text(utf8_encoded_value) + end + end +end diff --git a/spec/integration/translations_spec.rb b/spec/integration/translations_spec.rb deleted file mode 100644 index 9689ed3..0000000 --- a/spec/integration/translations_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'spec_helper' - -describe "Translations" do - def set_host (host) - host! host - Capybara.app_host = "http://" + host - end - - before(:each) do - set_host "translatable-qa.herokuapp.com" - Translation.send(:store).flushdb - end - - describe "GET /" do - before(:each) do - visit translations_url - end - - it "displays i18n locale filtering" do - page.should have_content("Locales") - page.should have_css("input#filter_by[type=text]") - end - - it "displays create key form" do - within(:css, 'form.new_translation') { - page.should have_css("label[for=translation_key]") - page.should have_css("label[for=translation_value]") - page.should have_css("input[type=submit]") - } - end - - it "displays key/value grid" do - page.should have_content("I18n Key") - page.should have_content("Value") - end - end - - describe "update translation" do - before(:each) do - @key = "foo" - @locale = 'en-US' - Translation.new(:locale => @locale, :key => @key, :value => 'bar').save - visit translations_url(:locale => @locale) - page.should have_css("tr[data-key=#{@key}]") - end - - it "updates the translation's value", :js => true do - within(:css, "tr[data-key=#{@key}]") { - find('span.editable').click - find('input[type=text]').set('baz') - find('button.save').click - } - visit translations_url(:locale => @locale) - - within(:css, "tbody tr[data-key=#{@key}]") { - find('td.key').text.should == @key - find('td.value span.editable').text.should == 'baz' - } - end - end - - describe "create translation" do - it "should create valid translation" do - visit translations_url - fill_in 'Key', :with => 'trankey' - fill_in 'Value', :with => 'tranvalue' - click_button 'Create' - - find_field('Key').value.should be_blank - find_field('Value').value.should be_blank - - within(:css, 'tbody tr') { - find('td.key').text.should == 'trankey' - find('td.value span.editable').text.should == 'tranvalue' - } - end - end -end diff --git a/spec/lib/redis_restore_spec.rb b/spec/lib/redis_restore_spec.rb new file mode 100644 index 0000000..886ab68 --- /dev/null +++ b/spec/lib/redis_restore_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe RedisRestore do + let(:src_rdb) { 'dump.rdb' } + let(:src_db) { 4 } + + describe :initialize do + describe :failure do + specify { lambda { RedisRestore.new() }.should raise_error('src_rdb is required') } + specify { lambda { RedisRestore.new(src_db: 3) }.should raise_error('src_rdb is required') } + specify { lambda { RedisRestore.new(src_rdb: src_rdb) }.should raise_error('src_db is required') } + end + end + + let(:expected_reloader_options) { {rdb_filename: src_rdb, + source_db: src_db, + redis_host: Settings.redis_host, + redis_port: Settings.redis_port, + target_db: Settings.redis_db} } + + subject { RedisRestore.new(src_rdb: src_rdb, src_db: src_db) } + + its(:reloader_options) { should eq(expected_reloader_options) } + + describe :restore do + it 'delegates to RedisReload' do + reload_stub = stub('reload', reload: 'yes!') + RedisReload::Reloader.should_receive(:new).with(expected_reloader_options).and_return(reload_stub) + subject.restore.should eq('yes!') + end + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 94e67d3..a94d0e7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,6 +6,9 @@ require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'capybara/rails' + require 'capybara/rspec' + require 'capybara/poltergeist' + require 'fakeredis/rspec' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -15,6 +18,5 @@ config.mock_with :rspec end - Capybara.javascript_driver = :webkit - + Capybara.javascript_driver = :poltergeist end