Make budgets translatable
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
class Admin::BudgetsController < Admin::BaseController
|
class Admin::BudgetsController < Admin::BaseController
|
||||||
|
include Translatable
|
||||||
include FeatureFlags
|
include FeatureFlags
|
||||||
feature_flag :budgets
|
feature_flag :budgets
|
||||||
|
|
||||||
@@ -56,8 +57,8 @@ class Admin::BudgetsController < Admin::BaseController
|
|||||||
|
|
||||||
def budget_params
|
def budget_params
|
||||||
descriptions = Budget::Phase::PHASE_KINDS.map{|p| "description_#{p}"}.map(&:to_sym)
|
descriptions = Budget::Phase::PHASE_KINDS.map{|p| "description_#{p}"}.map(&:to_sym)
|
||||||
valid_attributes = [:name, :phase, :currency_symbol] + descriptions
|
valid_attributes = [:phase, :currency_symbol] + descriptions
|
||||||
params.require(:budget).permit(*valid_attributes)
|
params.require(:budget).permit(*valid_attributes, translation_params(Budget))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,9 +3,14 @@ class Budget < ActiveRecord::Base
|
|||||||
include Measurable
|
include Measurable
|
||||||
include Sluggable
|
include Sluggable
|
||||||
|
|
||||||
|
translates :name, touch: true
|
||||||
|
include Globalizable
|
||||||
|
|
||||||
CURRENCY_SYMBOLS = %w(€ $ £ ¥).freeze
|
CURRENCY_SYMBOLS = %w(€ $ £ ¥).freeze
|
||||||
|
|
||||||
validates :name, presence: true, uniqueness: true
|
before_validation :assign_model_to_translations
|
||||||
|
|
||||||
|
validates_translation :name, presence: true
|
||||||
validates :phase, inclusion: { in: Budget::Phase::PHASE_KINDS }
|
validates :phase, inclusion: { in: Budget::Phase::PHASE_KINDS }
|
||||||
validates :currency_symbol, presence: true
|
validates :currency_symbol, presence: true
|
||||||
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
|
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
|
||||||
|
|||||||
11
app/models/budget/translation.rb
Normal file
11
app/models/budget/translation.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
class Budget::Translation < Globalize::ActiveRecord::Translation
|
||||||
|
validate :name_uniqueness_by_budget
|
||||||
|
|
||||||
|
def name_uniqueness_by_budget
|
||||||
|
if Budget.joins(:translations)
|
||||||
|
.where(name: name)
|
||||||
|
.where.not("budget_translations.budget_id": budget_id).any?
|
||||||
|
errors.add(:name, I18n.t("errors.messages.taken"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -8,6 +8,10 @@ module Globalizable
|
|||||||
def locales_not_marked_for_destruction
|
def locales_not_marked_for_destruction
|
||||||
translations.reject(&:_destroy).map(&:locale)
|
translations.reject(&:_destroy).map(&:locale)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def assign_model_to_translations
|
||||||
|
translations.each { |translation| translation.globalized_model = self }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
<%= form_for [:admin, @budget] do |f| %>
|
<%= render "admin/shared/globalize_locales", resource: @budget %>
|
||||||
|
|
||||||
|
<%= translatable_form_for [:admin, @budget] do |f| %>
|
||||||
|
|
||||||
|
<%= render 'shared/errors', resource: @budget %>
|
||||||
|
|
||||||
<div class="small-12 medium-9 column">
|
<div class="small-12 medium-9 column">
|
||||||
<%= f.text_field :name, maxlength: Budget.title_max_length %>
|
<%= f.translatable_fields do |translations_form| %>
|
||||||
|
<%= translations_form.text_field :name,
|
||||||
|
label: t("activerecord.attributes.budget.name"),
|
||||||
|
maxlength: Budget.title_max_length,
|
||||||
|
placeholder: t("activerecord.attributes.budget.name") %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="margin-top">
|
<div class="margin-top">
|
||||||
|
|||||||
@@ -26,14 +26,17 @@ end
|
|||||||
|
|
||||||
section "Creating Budgets" do
|
section "Creating Budgets" do
|
||||||
Budget.create(
|
Budget.create(
|
||||||
name: "#{I18n.t('seeds.budgets.budget')} #{Date.current.year - 1}",
|
name_en: "#{I18n.t("seeds.budgets.budget", locale: :en)} #{Date.current.year - 1}",
|
||||||
currency_symbol: I18n.t('seeds.budgets.currency'),
|
name_es: "#{I18n.t("seeds.budgets.budget", locale: :es)} #{Date.current.year - 1}",
|
||||||
phase: 'finished'
|
currency_symbol: I18n.t("seeds.budgets.currency"),
|
||||||
|
phase: "finished"
|
||||||
)
|
)
|
||||||
|
|
||||||
Budget.create(
|
Budget.create(
|
||||||
name: "#{I18n.t('seeds.budgets.budget')} #{Date.current.year}",
|
name_en: "#{I18n.t("seeds.budgets.budget", locale: :en)} #{Date.current.year}",
|
||||||
currency_symbol: I18n.t('seeds.budgets.currency'),
|
name_es: "#{I18n.t("seeds.budgets.budget", locale: :es)} #{Date.current.year}",
|
||||||
phase: 'accepting'
|
currency_symbol: I18n.t("seeds.budgets.currency"),
|
||||||
|
phase: "accepting"
|
||||||
)
|
)
|
||||||
|
|
||||||
Budget.all.each do |budget|
|
Budget.all.each do |budget|
|
||||||
|
|||||||
16
db/migrate/20190118135741_add_budget_translations.rb
Normal file
16
db/migrate/20190118135741_add_budget_translations.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
class AddBudgetTranslations < ActiveRecord::Migration
|
||||||
|
|
||||||
|
def self.up
|
||||||
|
Budget.create_translation_table!(
|
||||||
|
{
|
||||||
|
name: :string
|
||||||
|
},
|
||||||
|
{ migrate_data: true }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
Budget.drop_translation_table!
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
11
db/schema.rb
11
db/schema.rb
@@ -266,6 +266,17 @@ ActiveRecord::Schema.define(version: 20190205131722) do
|
|||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "budget_translations", force: :cascade do |t|
|
||||||
|
t.integer "budget_id", null: false
|
||||||
|
t.string "locale", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.string "name"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "budget_translations", ["budget_id"], name: "index_budget_translations_on_budget_id", using: :btree
|
||||||
|
add_index "budget_translations", ["locale"], name: "index_budget_translations_on_locale", using: :btree
|
||||||
|
|
||||||
create_table "budget_valuator_assignments", force: :cascade do |t|
|
create_table "budget_valuator_assignments", force: :cascade do |t|
|
||||||
t.integer "valuator_id"
|
t.integer "valuator_id"
|
||||||
t.integer "investment_id"
|
t.integer "investment_id"
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ feature 'Admin budgets' do
|
|||||||
login_as(admin.user)
|
login_as(admin.user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like "translatable",
|
||||||
|
"budget",
|
||||||
|
"edit_admin_budget_path",
|
||||||
|
%w[name]
|
||||||
|
|
||||||
context 'Feature flag' do
|
context 'Feature flag' do
|
||||||
|
|
||||||
background do
|
background do
|
||||||
@@ -95,7 +100,7 @@ feature 'Admin budgets' do
|
|||||||
visit admin_budgets_path
|
visit admin_budgets_path
|
||||||
click_link 'Create new budget'
|
click_link 'Create new budget'
|
||||||
|
|
||||||
fill_in 'budget_name', with: 'M30 - Summer campaign'
|
fill_in "Name", with: "M30 - Summer campaign"
|
||||||
select 'Accepting projects', from: 'budget[phase]'
|
select 'Accepting projects', from: 'budget[phase]'
|
||||||
|
|
||||||
click_button 'Create Budget'
|
click_button 'Create Budget'
|
||||||
@@ -112,6 +117,18 @@ feature 'Admin budgets' do
|
|||||||
expect(page).to have_css("label.error", text: "Name")
|
expect(page).to have_css("label.error", text: "Name")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario "Name should be unique" do
|
||||||
|
create(:budget, name: "Existing Name")
|
||||||
|
|
||||||
|
visit new_admin_budget_path
|
||||||
|
fill_in "Name", with: "Existing Name"
|
||||||
|
click_button "Create Budget"
|
||||||
|
|
||||||
|
expect(page).not_to have_content "New participatory budget created successfully!"
|
||||||
|
expect(page).to have_css("label.error", text: "Name")
|
||||||
|
expect(page).to have_css("small.error", text: "has already been taken")
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'Destroy' do
|
context 'Destroy' do
|
||||||
@@ -174,6 +191,31 @@ feature 'Admin budgets' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario "Changing name for current locale will update the slug if budget is in draft phase", :js do
|
||||||
|
budget.update(phase: "drafting")
|
||||||
|
old_slug = budget.slug
|
||||||
|
|
||||||
|
visit edit_admin_budget_path(budget)
|
||||||
|
|
||||||
|
select "Español", from: "translation_locale"
|
||||||
|
fill_in "Name", with: "Spanish name"
|
||||||
|
click_button "Update Budget"
|
||||||
|
|
||||||
|
expect(page).to have_content "Participatory budget updated successfully"
|
||||||
|
expect(budget.reload.slug).to eq old_slug
|
||||||
|
|
||||||
|
visit edit_admin_budget_path(budget)
|
||||||
|
|
||||||
|
click_link "English"
|
||||||
|
fill_in "Name", with: "New English Name"
|
||||||
|
click_button "Update Budget"
|
||||||
|
|
||||||
|
expect(page).to have_content "Participatory budget updated successfully"
|
||||||
|
expect(budget.reload.slug).not_to eq old_slug
|
||||||
|
expect(budget.slug).to eq "new-english-name"
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'Update' do
|
context 'Update' do
|
||||||
@@ -186,7 +228,7 @@ feature 'Admin budgets' do
|
|||||||
visit admin_budgets_path
|
visit admin_budgets_path
|
||||||
click_link 'Edit budget'
|
click_link 'Edit budget'
|
||||||
|
|
||||||
fill_in 'budget_name', with: 'More trees on the streets'
|
fill_in "Name", with: "More trees on the streets"
|
||||||
click_button 'Update Budget'
|
click_button 'Update Budget'
|
||||||
|
|
||||||
expect(page).to have_content('More trees on the streets')
|
expect(page).to have_content('More trees on the streets')
|
||||||
|
|||||||
@@ -8,11 +8,20 @@ describe Budget do
|
|||||||
|
|
||||||
describe "name" do
|
describe "name" do
|
||||||
before do
|
before do
|
||||||
create(:budget, name: 'object name')
|
budget.update(name_en: "object name")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is validated for uniqueness" do
|
it "must not be repeated for a different budget and same locale" do
|
||||||
expect(build(:budget, name: 'object name')).not_to be_valid
|
expect(build(:budget, name_en: "object name")).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "must not be repeated for a different budget and a different locale" do
|
||||||
|
expect(build(:budget, name_fr: "object name")).not_to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it "may be repeated for the same budget and a different locale" do
|
||||||
|
budget.update(name_fr: "object name")
|
||||||
|
expect(budget.translations.last).to be_valid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -329,6 +329,8 @@ def update_button_text
|
|||||||
"Update notification"
|
"Update notification"
|
||||||
when "Poll"
|
when "Poll"
|
||||||
"Update poll"
|
"Update poll"
|
||||||
|
when "Budget"
|
||||||
|
"Update Budget"
|
||||||
when "Poll::Question", "Poll::Question::Answer"
|
when "Poll::Question", "Poll::Question::Answer"
|
||||||
"Save"
|
"Save"
|
||||||
when "SiteCustomization::Page"
|
when "SiteCustomization::Page"
|
||||||
|
|||||||
Reference in New Issue
Block a user