diff --git a/Gemfile b/Gemfile index 615629f..8d5682f 100644 --- a/Gemfile +++ b/Gemfile @@ -31,6 +31,7 @@ gem 'fancybox-rails' gem 'friendly_id', '~> 4.0.0.beta14' gem 'simple-navigation' gem 'kaminari' +gem 'globalize3' group :development do gem 'rack-webconsole' diff --git a/Gemfile.lock b/Gemfile.lock index 9a2223b..9160fd8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,7 +38,7 @@ GEM addressable (2.2.6) arel (2.2.1) builder (3.0.0) - capybara (1.1.1) + capybara (1.1.2) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -49,7 +49,7 @@ GEM capybara (>= 1.0.0, < 1.2) carrierwave (0.5.7) activesupport (~> 3.0) - childprocess (0.2.2) + childprocess (0.3.0) ffi (~> 1.0.6) chunky_png (1.2.4) coderay (1.0.0) @@ -60,17 +60,16 @@ GEM css-bootstrap-rails (0.0.6) railties (~> 3.1) thor (~> 0.14) - cucumber (1.1.0) + cucumber (1.1.4) builder (>= 2.1.2) diff-lcs (>= 1.1.2) - gherkin (~> 2.5.0) + gherkin (~> 2.7.1) json (>= 1.4.6) term-ansicolor (>= 1.0.6) - cucumber-rails (0.5.2) - capybara (>= 1.0.0.rc1) - cucumber (>= 0.10.5) - nokogiri (>= 1.4.4) - rack-test (>= 0.5.7) + cucumber-rails (1.2.1) + capybara (>= 1.1.2) + cucumber (>= 1.1.3) + nokogiri (>= 1.5.0) database_cleaner (0.6.7) diff-lcs (1.1.3) erubis (2.7.0) @@ -79,7 +78,7 @@ GEM multi_json (~> 1.0) fancybox-rails (0.1.4) railties (>= 3.1.0) - ffi (1.0.9) + ffi (1.0.11) fog (1.1.1) builder excon (~> 0.7.4) @@ -96,10 +95,13 @@ GEM formalize-rails (0.0.4) railties (~> 3.1.0.rc1) formatador (0.2.1) - friendly_id (4.0.0.beta14) + friendly_id (4.0.0) fssm (0.2.7) - gherkin (2.5.1) + gherkin (2.7.4) json (>= 1.4.6) + globalize3 (0.1.0) + activemodel (>= 3.0.0) + activerecord (>= 3.0.0) growl (1.0.3) guard (0.7.0) thor (~> 0.14.6) @@ -127,8 +129,7 @@ GEM jquery-rails (1.0.14) railties (~> 3.0) thor (~> 0.14) - json (1.6.4) - json_pure (1.6.1) + json (1.6.5) kaminari (0.12.4) rails (>= 3.0.0) kgio (2.6.0) @@ -163,7 +164,7 @@ GEM method_source (>= 0.6.5) ruby_parser (>= 2.0.5) slop (~> 2.1.0) - rack (1.3.5) + rack (1.3.6) rack-cache (1.1) rack (>= 0.4) rack-mount (0.8.3) @@ -203,7 +204,7 @@ GEM ruby-hmac (0.4.0) ruby_parser (2.3.1) sexp_processor (~> 3.0) - rubyzip (0.9.4) + rubyzip (0.9.5) sass (3.1.7) sass-rails (3.1.2) actionpack (~> 3.1.0) @@ -211,10 +212,10 @@ GEM sass (>= 3.1.4) sprockets (~> 2.0.0) tilt (~> 1.3.2) - selenium-webdriver (2.7.0) - childprocess (>= 0.2.1) - ffi (>= 1.0.7) - json_pure + selenium-webdriver (2.17.0) + childprocess (>= 0.2.5) + ffi (~> 1.0.9) + multi_json (~> 1.0.4) rubyzip sexp_processor (3.0.7) simple-navigation (3.6.0) @@ -237,7 +238,7 @@ GEM sqlite3 (1.3.4) subexec (0.1.0) temple (0.3.4) - term-ansicolor (1.0.6) + term-ansicolor (1.0.7) thor (0.14.6) tilt (1.3.3) treetop (1.4.10) @@ -273,6 +274,7 @@ DEPENDENCIES foreman formalize-rails friendly_id (~> 4.0.0.beta14) + globalize3 growl guard guard-cucumber diff --git a/Guardfile b/Guardfile index ad7c2a2..a28de05 100644 --- a/Guardfile +++ b/Guardfile @@ -4,6 +4,7 @@ end guard 'minitest', drb: false, rubygems: false, bundler: false, notify: false do watch(%r{app/(.+)/(.+)\.rb}) { |m| "spec/#{m[1]}/#{m[2]}_spec.rb" } + watch(%r{lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%r{spec/(.+)/(.+)_spec\.rb}) watch(%r{spec/spec_helper.rb}) { "test" } end diff --git a/app/assets/stylesheets/partials/_layout.sass b/app/assets/stylesheets/partials/_layout.sass index 448bcd6..73cedd1 100644 --- a/app/assets/stylesheets/partials/_layout.sass +++ b/app/assets/stylesheets/partials/_layout.sass @@ -4,7 +4,7 @@ $silvia-color: #741519 $menu-color: #666666 #sidebar - position: fixed + position: absolute top: 0px left: 0px diff --git a/app/assets/stylesheets/partials/_sidebar.sass b/app/assets/stylesheets/partials/_sidebar.sass index 63e54fb..414a2e2 100644 --- a/app/assets/stylesheets/partials/_sidebar.sass +++ b/app/assets/stylesheets/partials/_sidebar.sass @@ -14,7 +14,7 @@ height: 352px display: block - ul#sidebar_navigation li + ul#sidebar_navigation li, ul#language li +horizontal-list margin-bottom: 10px padding: 9px @@ -38,8 +38,12 @@ &:hover color: $silvia-color + ul#language + margin-top: 15px + ul#social list-style-type: none + height: 30px li a float: left diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4399b57..363212e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,8 +1,10 @@ -require "application_responder" +require 'application_responder' +require 'locale_detector' class ApplicationController < ActionController::Base self.responder = ApplicationResponder respond_to :html + before_filter :set_locale protect_from_forgery @@ -18,4 +20,9 @@ def select_layout def pjax_request? request.headers['X-PJAX'] end + + def set_locale + session[:locale] = LocaleDetector.new(params, session, request.env).detect + I18n.locale = session[:locale] + end end diff --git a/app/controllers/contact_forms_controller.rb b/app/controllers/contact_forms_controller.rb index 9d6fa52..f2a2003 100644 --- a/app/controllers/contact_forms_controller.rb +++ b/app/controllers/contact_forms_controller.rb @@ -7,7 +7,7 @@ def create @contact_form = ContactForm.new(params[:contact_form]) @contact_form.request = request if @contact_form.deliver - flash.now[:notice] = 'Thank you for contacting me! You will be receiving an answer soon' + flash.now[:notice] = t('contact_forms.thank_you') end render action: 'new' end diff --git a/app/models/category.rb b/app/models/category.rb index 11f0c45..4820248 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,15 +1,36 @@ # Categories are used to classify Illustrations and Videos class Category < ActiveRecord::Base extend FriendlyId - friendly_id :name, use: :slugged + include GlobalizeExtensions + + friendly_id :name, use: :simple_i18n validates :name, presence: true has_many :illustrations, dependent: :nullify has_many :videos, dependent: :nullify + translates :name + translate_accessors_in :ca, :es + before_save :set_friendly_id + # A simple scope to just return active categories # def self.active where(active: true) end + + # Never generate automatically a new slug because it has problems with + # multiple locales. + def should_generate_new_friendly_id? + false + end + + # Set the slug in all available locales + def set_friendly_id(*args) + I18n.available_locales.each do |locale| + I18n.with_locale(locale) do + super(name, locale) + end + end + end end diff --git a/app/models/concerns/globalize_extensions.rb b/app/models/concerns/globalize_extensions.rb new file mode 100644 index 0000000..59de71f --- /dev/null +++ b/app/models/concerns/globalize_extensions.rb @@ -0,0 +1,23 @@ +module GlobalizeExtensions + extend ActiveSupport::Concern + + included do + def self.translate_accessors_in(*locales) + locales.each do |locale| + translated_attribute_names.each do |field| + define_method("#{field}_#{locale}") do + ::Globalize.with_locale(locale) do + self.send("#{field}") + end + end + + define_method("#{field}_#{locale}=") do |value| + ::Globalize.with_locale(locale) do + self.send("#{field}=", value) + end + end + end + end + end + end +end diff --git a/app/models/illustration.rb b/app/models/illustration.rb index cca9373..393f4f1 100644 --- a/app/models/illustration.rb +++ b/app/models/illustration.rb @@ -1,9 +1,10 @@ # An Illustration is a media type with a picture attached -# class Illustration < ActiveRecord::Base include Thumbnailer + include GlobalizeExtensions extend FriendlyId - friendly_id :name, use: :slugged + + friendly_id :name, use: :simple_i18n default_scope order('created_at') @@ -14,6 +15,10 @@ class Illustration < ActiveRecord::Base # A category is needed in order to classify the illustration belongs_to :category + translates :name, :description + translate_accessors_in :ca, :es + before_save :set_friendly_id + # A scope to get illustration that belong to a category # # category - The Category that illustration shoudl belong to @@ -22,4 +27,19 @@ class Illustration < ActiveRecord::Base def self.by_category(category) where(category_id: category) end + + # Never generate automatically a new slug because it has problems with + # multiple locales. + def should_generate_new_friendly_id? + false + end + + # Set the slug in all available locales + def set_friendly_id(*args) + I18n.available_locales.each do |locale| + I18n.with_locale(locale) do + super(name, locale) + end + end + end end diff --git a/app/models/video.rb b/app/models/video.rb index 1ebce03..83df104 100644 --- a/app/models/video.rb +++ b/app/models/video.rb @@ -1,6 +1,10 @@ # A Video is a media type with a linked video to an external service class Video < ActiveRecord::Base + include GlobalizeExtensions + belongs_to :category + validates :name, :url, :category, presence: true - belongs_to :category + translates :name, :description + translate_accessors_in :ca, :es end diff --git a/app/views/backend/categories/_form.html.slim b/app/views/backend/categories/_form.html.slim new file mode 100644 index 0000000..956cdfa --- /dev/null +++ b/app/views/backend/categories/_form.html.slim @@ -0,0 +1,5 @@ += simple_form_for [ :backend, resource ] do |f| + = f.input :name_ca + = f.input :name_es + = f.input :active + = f.submit diff --git a/app/views/backend/illustrations/_form.html.slim b/app/views/backend/illustrations/_form.html.slim index 9d9d99e..7a64183 100644 --- a/app/views/backend/illustrations/_form.html.slim +++ b/app/views/backend/illustrations/_form.html.slim @@ -1,6 +1,8 @@ = simple_form_for [ :backend, resource ] do |f| - - (attributes - ['image', 'category_id', 'slug']).each do |attr| - = f.input attr + = f.input :name_ca, required: true + = f.input :name_es, required: true + = f.input :description_ca, as: :text + = f.input :description_es, as: :text #illustration = f.input :image diff --git a/app/views/backend/illustrations/_show.html.slim b/app/views/backend/illustrations/_show.html.slim index 79f3249..290fa7f 100644 --- a/app/views/backend/illustrations/_show.html.slim +++ b/app/views/backend/illustrations/_show.html.slim @@ -1,5 +1,5 @@ dl - - (attributes - %w(image thumbnail_coordinates category_id)).each do |attr| + - (attributes - %w(image thumbnail_coordinates category_id) + %w(name_ca name_es description_ca description_es)).each do |attr| dt= resource_class.human_attribute_name(attr) dd= resource.public_send(attr) dt= resource_class.human_attribute_name(:image) diff --git a/app/views/backend/resource/new.html.slim b/app/views/backend/resource/new.html.slim index 2f28f34..87c2bc7 100644 --- a/app/views/backend/resource/new.html.slim +++ b/app/views/backend/resource/new.html.slim @@ -1,6 +1,6 @@ .block .content - h2 New #{resource_class.model_name.human} + h2=resource_class.model_name.human .inner = render "form", attributes: attributes diff --git a/app/views/backend/videos/_form.html.slim b/app/views/backend/videos/_form.html.slim new file mode 100644 index 0000000..962caf4 --- /dev/null +++ b/app/views/backend/videos/_form.html.slim @@ -0,0 +1,9 @@ += simple_form_for [ :backend, resource ] do |f| + = f.input :name_ca, required: true + = f.input :name_es, required: true + = f.input :description_ca, as: :text + = f.input :description_es, as: :text + = f.input :url + = f.association :category, collection: Category.all + = f.submit + diff --git a/app/views/contact_forms/new.html.slim b/app/views/contact_forms/new.html.slim index 0be18d7..611ae61 100644 --- a/app/views/contact_forms/new.html.slim +++ b/app/views/contact_forms/new.html.slim @@ -5,7 +5,7 @@ section#contact section#contact-form = twitter_bootstrap_form_for @contact_form, url: contact_path, validate: false do |f| - =f.inputs 'Contacte' do + =f.inputs t('.title') do =f.text_field :name =f.email_field :email, placeholder: 'email@example.org' =f.text_area :message, class: 'xxlarge' diff --git a/app/views/layouts/_sidebar.html.slim b/app/views/layouts/_sidebar.html.slim index b4b32d4..2599c13 100644 --- a/app/views/layouts/_sidebar.html.slim +++ b/app/views/layouts/_sidebar.html.slim @@ -5,3 +5,4 @@ h1 nav ==render_navigation(context: :sidebar) ==render_navigation(context: :social) + ==render_navigation(context: :language) diff --git a/config/application.rb b/config/application.rb index 9ab2aa2..e46993c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -18,6 +18,7 @@ class Application < Rails::Application # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths += %W( #{config.root}/app/services + #{config.root}/app/models/concerns ) # Only load the plugins named here, in the order given (default is alphabetical). @@ -34,6 +35,7 @@ class Application < Rails::Application # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] config.i18n.default_locale = :ca + config.i18n.fallbacks = true # Please note that JavaScript expansions are *ignored altogether* if the asset # pipeline is enabled (see config.assets.enabled below). Put your defaults in diff --git a/config/initializers/i18n.rb b/config/initializers/i18n.rb new file mode 100644 index 0000000..f0deb02 --- /dev/null +++ b/config/initializers/i18n.rb @@ -0,0 +1 @@ +I18n.available_locales = [:ca, :es] diff --git a/config/language_navigation.rb b/config/language_navigation.rb new file mode 100644 index 0000000..75295c8 --- /dev/null +++ b/config/language_navigation.rb @@ -0,0 +1,10 @@ +#encoding: utf-8 + +SimpleNavigation::Configuration.run do |navigation| + navigation.items do |primary| + primary.dom_id = 'language' + primary.item 'ca', 'Català', request.path + '?locale=ca' + primary.item 'es', 'Español', request.path + '?locale=es' + end +end + diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 5e3687c..e8f086c 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -6,13 +6,25 @@ ca: category: Categoria categories: Categories attributes: + video: + name: Nom + name_ca: Nom (català) + name_es: Nom (castellà) + description_ca: Descripció (català) + description_es: Descripció (castellà) + category: Categoria illustration: name: Nom - description: Descripció + name_ca: Nom (català) + name_es: Nom (castellà) + description_ca: Descripció (català) + description_es: Descripció (castellà) image: Imatge category: Categoria category: name: Nom + name_ca: Nom (català) + name_es: Nom (castellà) active: Activa flash: actions: @@ -25,6 +37,10 @@ ca: destroy: notice: '%{resource_name} was successfully destroyed.' alert: '%{resource_name} could not be destroyed.' + contact_forms: + new: + title: 'Contacte' + thank_you: 'Gràcies per posar-te en contacte amb mi. Aviat rebràs una resposta!' helpers: label: contact_form: @@ -34,6 +50,10 @@ ca: submit: contact_form: create: 'Enviar' + navigation: + sidebar: + about: 'qui sóc?' + contact: 'contacte' views: pagination: previous: "← Anterior" diff --git a/config/locales/es.yml b/config/locales/es.yml new file mode 100644 index 0000000..26859bc --- /dev/null +++ b/config/locales/es.yml @@ -0,0 +1,24 @@ +es: + contact_forms: + new: + title: 'Contacto' + thank_you: 'Gracias por ponerte en contacto conmigo. ¡Pronto recibirás una respuesta!' + helpers: + label: + contact_form: + name: Nombre + email: Correo electrónico + message: Mensaje + submit: + contact_form: + create: 'Enviar' + navigation: + sidebar: + about: '¿quién soy?' + contact: 'contacto' + views: + pagination: + previous: "← Anterior" + next: "Siguiente →" + truncate: "..." + diff --git a/config/sidebar_navigation.rb b/config/sidebar_navigation.rb index 53e05fa..59e974a 100644 --- a/config/sidebar_navigation.rb +++ b/config/sidebar_navigation.rb @@ -3,8 +3,8 @@ SimpleNavigation::Configuration.run do |navigation| navigation.items do |primary| primary.dom_id = 'sidebar_navigation' - primary.item 'about', 'qui sóc?', page_path('about') - primary.item 'contact', 'contacte', new_contact_path + primary.item 'about', I18n.t('navigation.sidebar.about'), page_path('about') + primary.item 'contact', I18n.t('navigation.sidebar.contact'), new_contact_path primary.item 'phone', '668 860 931' end end diff --git a/db/migrate/20120123122809_create_globalize_tables.rb b/db/migrate/20120123122809_create_globalize_tables.rb new file mode 100644 index 0000000..8c3bbbb --- /dev/null +++ b/db/migrate/20120123122809_create_globalize_tables.rb @@ -0,0 +1,29 @@ +class CreateGlobalizeTables < ActiveRecord::Migration + def up + Category.create_translation_table!({ + name: :string, + }, { + migrate_data: true + }) + + Illustration.create_translation_table!({ + name: :string, + description: :text + }, { + migrate_data: true + }) + + Video.create_translation_table!({ + name: :string, + description: :text + }, { + migrate_data: true + }) + end + + def down + Category.drop_translation_table! + Illustration.drop_translation_table! + Video.drop_translation_table! + end +end diff --git a/db/migrate/20120123183023_translate_slugs.rb b/db/migrate/20120123183023_translate_slugs.rb new file mode 100644 index 0000000..1c49975 --- /dev/null +++ b/db/migrate/20120123183023_translate_slugs.rb @@ -0,0 +1,27 @@ +class TranslateSlugs < ActiveRecord::Migration + def up + rename_column :illustrations, :slug, :slug_ca + add_column :illustrations, :slug_es, :string + add_index :illustrations, :slug_es + + rename_column :categories, :slug, :slug_ca + add_column :categories, :slug_es, :string + add_index :categories, :slug_es + + add_column :videos, :slug_ca, :string + add_column :videos, :slug_es, :string + add_index :videos, :slug_ca + add_index :videos, :slug_es + end + + def down + rename_column :illustrations, :slug_ca, :slug + remove_column :illustrations, :slug_es + + rename_column :categories, :slug_ca, :slug + remove_column :categories, :slug_es + + remove_column :videos, :slug_ca + remove_column :videos, :slug_es + end +end diff --git a/db/migrate/20120123184625_copy_catalan_slugs_to_spanish.rb b/db/migrate/20120123184625_copy_catalan_slugs_to_spanish.rb new file mode 100644 index 0000000..7e9ee14 --- /dev/null +++ b/db/migrate/20120123184625_copy_catalan_slugs_to_spanish.rb @@ -0,0 +1,15 @@ +class CopyCatalanSlugsToSpanish < ActiveRecord::Migration + def up + Illustration.find_each do |record| + record.set_friendly_id(record.slug_ca, :es) + end + + Category.find_each do |record| + record.set_friendly_id(record.slug_ca, :es) + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20120123190025_remove_translated_columns.rb b/db/migrate/20120123190025_remove_translated_columns.rb new file mode 100644 index 0000000..ee4e99b --- /dev/null +++ b/db/migrate/20120123190025_remove_translated_columns.rb @@ -0,0 +1,13 @@ +class RemoveTranslatedColumns < ActiveRecord::Migration + def up + remove_column :categories, :name + remove_column :illustrations, :name + remove_column :illustrations, :description + remove_column :videos, :name + remove_column :videos, :description + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/schema.rb b/db/schema.rb index 95e855d..df55fb5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,39 +11,74 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20111206190441) do +ActiveRecord::Schema.define(:version => 20120123190025) do create_table "categories", :force => true do |t| - t.string "name" t.datetime "created_at" t.datetime "updated_at" t.boolean "active" - t.string "slug" + t.string "slug_ca" + t.string "slug_es" end - add_index "categories", ["name"], :name => "index_categories_on_name" - add_index "categories", ["slug"], :name => "index_categories_on_slug" + add_index "categories", ["slug_ca"], :name => "index_categories_on_slug" + add_index "categories", ["slug_es"], :name => "index_categories_on_slug_es" - create_table "illustrations", :force => true do |t| + create_table "category_translations", :force => true do |t| + t.integer "category_id" + t.string "locale" + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "category_translations", ["category_id"], :name => "index_category_translations_on_category_id" + + create_table "illustration_translations", :force => true do |t| + t.integer "illustration_id" + t.string "locale" t.string "name" t.text "description" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "illustration_translations", ["illustration_id"], :name => "index_b0c98806d31be981f03b71bdd325c9417e68cd00" + + create_table "illustrations", :force => true do |t| t.string "image" t.text "thumbnail_coordinates" t.integer "category_id" t.datetime "created_at" t.datetime "updated_at" - t.string "slug" + t.string "slug_ca" + t.string "slug_es" end - add_index "illustrations", ["slug"], :name => "index_illustrations_on_slug", :unique => true + add_index "illustrations", ["slug_ca"], :name => "index_illustrations_on_slug", :unique => true + add_index "illustrations", ["slug_es"], :name => "index_illustrations_on_slug_es" - create_table "videos", :force => true do |t| + create_table "video_translations", :force => true do |t| + t.integer "video_id" + t.string "locale" t.string "name" t.text "description" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "video_translations", ["video_id"], :name => "index_video_translations_on_video_id" + + create_table "videos", :force => true do |t| t.string "url" t.integer "category_id" t.datetime "created_at" t.datetime "updated_at" + t.string "slug_ca" + t.string "slug_es" end + add_index "videos", ["slug_ca"], :name => "index_videos_on_slug_ca" + add_index "videos", ["slug_es"], :name => "index_videos_on_slug_es" + end diff --git a/features/illustrations/visitor_lists_illustrations.feature b/features/illustrations/visitor_lists_illustrations.feature index b3e7994..ac50182 100644 --- a/features/illustrations/visitor_lists_illustrations.feature +++ b/features/illustrations/visitor_lists_illustrations.feature @@ -23,12 +23,6 @@ Feature: Visitor views illustrations When I click at the illustration bigger picture Then I should see a high-res illustration picture - Scenario: Visitor views more illustrations using the pagination - Given there are even more illustrations - And I am on the homepage - When I click the pagination link to view more illustrations - Then I should see new illustrations - Scenario: Visitor views categorized illustrations Given some illustration are categorized with fanart And I am on the homepage diff --git a/features/step_definitions/category_steps.rb b/features/step_definitions/category_steps.rb index cc1afba..673a5be 100644 --- a/features/step_definitions/category_steps.rb +++ b/features/step_definitions/category_steps.rb @@ -7,7 +7,7 @@ end Then /^I should see these categories at the main menu$/ do - within '#header .menu' do + within '#header nav' do @categories.each do |category| page.has_link?(category.name).must_equal true end @@ -19,7 +19,7 @@ end Then /^I should not see these categories$/ do - within '#header .menu' do + within '#header nav' do @categories.each do |category| page.has_link?(category.name).must_equal false end diff --git a/features/step_definitions/contact_steps.rb b/features/step_definitions/contact_steps.rb index 8aed8c4..da77ddf 100644 --- a/features/step_definitions/contact_steps.rb +++ b/features/step_definitions/contact_steps.rb @@ -12,7 +12,7 @@ end Then /^I should see a message telling me that the contact request has been sent$/ do - must_have_confirmation_message('Thank you for contacting me! You will be receiving an answer soon') + must_have_confirmation_message('Gràcies per posar-te en contacte amb mi. Aviat rebràs una resposta!') end Then /^Silvia should receive an email with the contact form information$/ do diff --git a/features/step_definitions/illustration_steps.rb b/features/step_definitions/illustration_steps.rb index 8c73b19..6ea596e 100644 --- a/features/step_definitions/illustration_steps.rb +++ b/features/step_definitions/illustration_steps.rb @@ -6,8 +6,10 @@ end When /^I fill the illustration form with all the necessary data$/ do - fill_in 'Nom', with: 'Course of witches' - fill_in 'Descripció', with: 'Personatge per un còmic' + fill_in 'Nom (català)', with: 'Course of witches' + fill_in 'Nom (castellà)', with: 'Course of witches castellà' + fill_in 'Descripció (català)', with: 'Personatge per un còmic' + fill_in 'Descripció (castellà)', with: 'Personatge per un còmic' attach_file 'Imatge', Rails.root.join('spec/support/files/illustration.jpg') select(@current_category.name, from: 'Categoria') click_button 'Crear Il·lustració' diff --git a/features/step_definitions/locale_steps.rb b/features/step_definitions/locale_steps.rb new file mode 100644 index 0000000..d037579 --- /dev/null +++ b/features/step_definitions/locale_steps.rb @@ -0,0 +1,29 @@ +#encoding: utf-8 + +When /^I follow Español in the language menu$/ do + within '#language' do + click_link 'Español' + end +end + +Then /^I should see the page in Spanish$/ do + page.has_content?('¿quién soy?').must_equal true + page.has_content?('contacto').must_equal true +end + +Given /^I am seeing the web in spanish$/ do + within '#language' do + click_link 'Español' + end +end + +When /^I follow Català in the language menu$/ do + within '#language' do + click_link 'Català' + end +end + +Then /^I should see the page in Catalan$/ do + page.has_content?('qui sóc?').must_equal true + page.has_content?('contacte').must_equal true +end diff --git a/features/step_definitions/visitor_steps.rb b/features/step_definitions/visitor_steps.rb index 141a475..361308f 100644 --- a/features/step_definitions/visitor_steps.rb +++ b/features/step_definitions/visitor_steps.rb @@ -8,7 +8,7 @@ end Then /^I should see the illustrations$/ do - page.has_css?('li.thumbnail', count: @illustrations.length).must_equal true + page.has_css?('#gallery #works li', count: @illustrations.length).must_equal true end Then /^I should see the expanded view of the newest illustration$/ do @@ -43,18 +43,6 @@ page.has_css?('img#fancybox-img').must_equal true end -Given /^there are even more illustrations$/ do - @illustrations = @illustrations + Illustration.make!(5) -end - -When /^I click the pagination link to view more illustrations$/ do - click_link 'Següent' -end - -Then /^I should see new illustrations$/ do - must_see_illustration(@illustrations.last) -end - Given /^some illustration are categorized with fanart$/ do @fanart_illustration = Illustration.make!(category: Category.make(name: 'Fanart')) end diff --git a/features/support/env.rb b/features/support/env.rb index d87b487..18c974e 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -23,7 +23,17 @@ end begin - DatabaseCleaner.strategy = :transaction + require 'database_cleaner' + require 'database_cleaner/cucumber' + DatabaseCleaner.strategy = :truncation rescue NameError raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it." end + +Before do + DatabaseCleaner.start +end + +After do |scenario| + DatabaseCleaner.clean +end diff --git a/features/user_changes_locale.feature b/features/user_changes_locale.feature new file mode 100644 index 0000000..38f737e --- /dev/null +++ b/features/user_changes_locale.feature @@ -0,0 +1,17 @@ +#encoding: utf-8 + +Feature: Change locale + As a user + I want to change the locale + So I can read the content in my preferred language + + Scenario: User changes the locale to Spanish + Given I am on the homepage + When I follow Español in the language menu + Then I should see the page in Spanish + + Scenario: User switches back to Catalan + Given I am on the homepage + And I am seeing the web in spanish + When I follow Català in the language menu + Then I should see the page in Catalan diff --git a/lib/locale_detector.rb b/lib/locale_detector.rb new file mode 100644 index 0000000..d691c85 --- /dev/null +++ b/lib/locale_detector.rb @@ -0,0 +1,65 @@ +require 'i18n' + +# Public: Detect the locale from the current user using different strategies. +# +# Examples: +# +# +# class ApplicationController < ActionController::Base +# before_filter :set_locale +# +# def set_locale +# session[:locale] = LocaleDetector.new(params, session, request.env).detect +# I18n.locale = session[:locale] +# end +# end +# +class LocaleDetector + + # Public: Initialize a LocaleDetector + # + # params - A Hash with a String key named locale + # session - A Hash with a Symbol key named :locale + # request - A Hash with a String key named HTTP_ACCEPT_LANGUAGE + def initialize(params, session, request) + @params, @session, @request = params, session, request + end + + # Public: Detect the locale. + # It will try to detect it first from the params, then try the session, then + # from the request. If all fails fallback to I18n default locale + # + # Returns a Symbol representing the locale + def detect + detect_from_params || detect_from_session || detect_from_request || I18n.default_locale + end + + # Private: Detect the locale from the params + # + # Returns a Symbol representing the locale or nil if none was found + def detect_from_params + @params['locale'].to_sym if available?(@params['locale']) + end + + # Private: Detect the locale from the session + # + # Returns a Symbol representing the locale or nil if none was found + def detect_from_session + @session[:locale].to_sym if available?(@session[:locale]) + end + + # Private: Detect the locale from the HTTP request. + # It will try to find an available locale from all the accepted by the user. + # + # Returns a Symbol representing the locale or nil if none was found + def detect_from_request + locales = @request['HTTP_ACCEPT_LANGUAGE'].to_s.scan(/[a-z]{2}/).uniq + locale = locales.detect { |locale| available?(locale) } + locale.to_sym if locale + end + + private + def available?(locale) + I18n.available_locales.include?(locale.to_sym) if locale + end +end diff --git a/spec/database_cleaner_helper.rb b/spec/database_cleaner_helper.rb new file mode 100644 index 0000000..3242d99 --- /dev/null +++ b/spec/database_cleaner_helper.rb @@ -0,0 +1,10 @@ +DatabaseCleaner.strategy = :truncation +class MiniTest::Spec + before do + DatabaseCleaner.start + end + + after do + DatabaseCleaner.clean + end +end diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb index 8b71c9d..364fbca 100644 --- a/spec/fast_spec_helper.rb +++ b/spec/fast_spec_helper.rb @@ -7,6 +7,12 @@ def setup_active_record unless ActiveRecord::Base.connected? ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'db/test.sqlite') end + setup_database_cleaner +end + +def setup_database_cleaner + require 'database_cleaner' + require_relative 'database_cleaner_helper' end def require_model(file) @@ -24,6 +30,14 @@ def require_service(file) require_relative "../app/services/#{file}" end +def require_lib(file) + require_relative "../lib/#{file}" +end + +def require_concern(file) + require_relative "../app/models/concerns/#{file}" +end + def require_carrierwave require 'carrierwave' require 'carrierwave/orm/activerecord' diff --git a/spec/lib/locale_detector_spec.rb b/spec/lib/locale_detector_spec.rb new file mode 100644 index 0000000..d98c5a4 --- /dev/null +++ b/spec/lib/locale_detector_spec.rb @@ -0,0 +1,84 @@ +require 'fast_spec_helper' +require_lib 'locale_detector' + +describe LocaleDetector do + let(:params) { {'locale' => 'ca' }} + let(:session) { {locale: :ca }} + let(:request) { {'HTTP_ACCEPT_LANGUAGE' => 'en-US,en,ca;q=0.8'} } + + subject { LocaleDetector.new(params, session, request) } + + before do + I18n.available_locales = [:ca, :es] + I18n.default_locale = :de + I18n.locale = :ch + end + + describe 'detect_from_params' do + it 'returns the locale from the params' do + subject.detect_from_params.must_equal :ca + end + + it 'returns nil if the locale is not available' do + I18n.available_locales = [:en, :de] + + subject.detect_from_params.must_equal nil + end + end + + describe 'detect_from_session' do + it 'returns the locale from the session' do + subject.detect_from_session.must_equal :ca + end + + it 'returns nil if the locale is not available' do + I18n.available_locales = [:en, :de] + + subject.detect_from_params.must_equal nil + end + end + + describe 'detect_from_request' do + it 'returns the first available locale' do + subject.detect_from_request.must_equal :ca + end + + it 'returns nil when no header' do + subject = LocaleDetector.new({}, {}, {}) + + subject.detect_from_request.must_equal nil + end + + it 'returns nil if the locale is not available' do + I18n.available_locales = [:es, :ch] + + subject.detect_from_request.must_equal nil + end + end + + describe 'detect' do + it "gets the default locale if can't find anything" do + subject = LocaleDetector.new({}, {}, {}) + + subject.detect.must_equal :de + end + + it 'gets the locale from the params' do + subject = LocaleDetector.new(params, {}, {}) + + subject.detect.must_equal :ca + end + + it 'gets the locale from the session if params is nil' do + subject = LocaleDetector.new({}, session, {}) + + subject.detect.must_equal :ca + end + + it 'gets the locale from the request if params and session is nil' do + detector = LocaleDetector.new({}, {}, request) + + subject.detect.must_equal :ca + end + end +end diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 6e0c160..7f3bb46 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -1,9 +1,14 @@ require 'fast_spec_helper' require 'friendly_id' +require 'globalize3' +I18n.available_locales = [:ca, :es] +I18n.locale = :ca +require_concern 'globalize_extensions' require_model 'category' require_uploader 'image_uploader' require_service 'thumbnailer' require_model 'illustration' +require_model 'video' describe Category do @@ -18,6 +23,16 @@ end end + describe 'translations' do + it 'translaters setters in catalan' do + subject.respond_to?(:name_ca=).must_equal true + end + + it 'translaters setters in spanish' do + subject.respond_to?(:name_es=).must_equal true + end + end + describe 'relations' do it 'has many illustrations' do %w(illustration_ids illustration_ids= illustrations illustrations=).each do |method| @@ -26,8 +41,10 @@ end it 'nullifies its children when deleted' do - subject = Category.make! - child = Illustration.make!(category: subject) + subject = Category.make + subject.save! + child = Illustration.make(category: subject) + child.save! subject.destroy @@ -39,13 +56,15 @@ describe 'scopes' do describe 'active' do it 'includes active categories' do - @category = Category.make!(active: true) + @category = Category.make(active: true) + @category.save! Category.active.must_include @category end it 'does not include inactive categories' do - @category = Category.make!(active: false) + @category = Category.make(active: false) + @category.save! Category.active.wont_include @category end diff --git a/spec/models/concerns/globalize_extenstions_spec.rb b/spec/models/concerns/globalize_extenstions_spec.rb new file mode 100644 index 0000000..733ce9b --- /dev/null +++ b/spec/models/concerns/globalize_extenstions_spec.rb @@ -0,0 +1,51 @@ +require 'fast_spec_helper' +require 'mocha' +require 'globalize' +require_concern 'globalize_extensions' + +class TestGlobalizeExtensions + include GlobalizeExtensions + + def self.translated_attribute_names + ['name', 'description'] + end + + translate_accessors_in :ca, :es +end + +describe GlobalizeExtensions do + subject { TestGlobalizeExtensions.new } + + describe 'translate_setters_in' do + it 'define a setter for each translated attribute and locale' do + subject.respond_to?(:name_ca=).must_equal true + subject.respond_to?(:name_es=).must_equal true + subject.respond_to?(:description_ca=).must_equal true + subject.respond_to?(:description_es=).must_equal true + end + + it 'defines a getter using Globalize to get the value in a given locale' do + Globalize.expects(:with_locale).with(:ca) + + subject.name_ca + end + + it 'defines a getter to get the translated value ' do + subject.expects(:name).returns('caca') + + subject.name_ca.must_equal 'caca' + end + + it 'defines a setter using Globalize to set the value in a given locale' do + Globalize.expects(:with_locale).with(:ca) + + subject.name_ca = 'caca' + end + + it 'defines a setter to call the original setter' do + subject.expects(:name=).with('caca') + + subject.name_ca = 'caca' + end + end +end diff --git a/spec/models/illustration_spec.rb b/spec/models/illustration_spec.rb index 21532ce..f1b063e 100644 --- a/spec/models/illustration_spec.rb +++ b/spec/models/illustration_spec.rb @@ -1,9 +1,13 @@ -require_relative '../fast_spec_helper' +require 'fast_spec_helper' +require 'friendly_id' +require 'globalize3' +I18n.available_locales = [:ca, :es] +I18n.locale = :ca +require_concern 'globalize_extensions' +require_model 'category' require_uploader 'image_uploader' require_service 'thumbnailer' -require 'friendly_id' require_model 'illustration' -require_model 'category' describe Illustration do subject { Illustration.new } @@ -53,19 +57,47 @@ describe 'scopes' do describe 'by_category' do it 'includes illustrations from a given category' do - category = Category.make! - illustration = Illustration.make!(category: category) + category = Category.make + category.save! + illustration = Illustration.make(category: category) + illustration.save! Illustration.by_category(category).must_include illustration end it 'does not include illustrations from a other categoris' do - category = Category.make! - another_category = Category.make! - illustration = Illustration.make!(category: another_category) + another_category = Category.make + another_category.save! + category = Category.make + category.save! + illustration = Illustration.make(category: another_category) + illustration.save! Illustration.by_category(category).wont_include illustration end end end + + describe 'translations' do + it 'translaters setters in catalan' do + subject.respond_to?(:name_ca=).must_equal true + subject.respond_to?(:description_ca=).must_equal true + end + + it 'translaters setters in spanish' do + subject.respond_to?(:name_es=).must_equal true + subject.respond_to?(:description_es=).must_equal true + end + end + + describe 'set_friendly_id' do + it 'sets the slug in every available locale' do + illustration = Illustration.new(name_ca: 'catalan', name_es: 'spanish') + + illustration.set_friendly_id + + illustration.slug_ca.must_equal 'catalan' + illustration.slug_es.must_equal 'spanish' + end + end end diff --git a/spec/models/video_spec.rb b/spec/models/video_spec.rb index 4853b4a..ace837a 100644 --- a/spec/models/video_spec.rb +++ b/spec/models/video_spec.rb @@ -1,5 +1,10 @@ require 'fast_spec_helper' +require 'friendly_id' +require 'globalize3' +require_concern 'globalize_extensions' require_model 'category' +require_uploader 'image_uploader' +require_service 'thumbnailer' require_model 'video' describe Video do @@ -35,4 +40,16 @@ end end end + + describe 'translations' do + it 'translaters setters in catalan' do + subject.respond_to?(:name_ca=).must_equal true + subject.respond_to?(:description_ca=).must_equal true + end + + it 'translaters setters in spanish' do + subject.respond_to?(:name_es=).must_equal true + subject.respond_to?(:description_es=).must_equal true + end + end end