I'm having some issues with configuring the mobility gem.
If a translation isn't present, I would like to get the default translation (in this case :de should fallback to :en). I built a test which should explain what I'm struggling with. I guess it's a misconfiguration, hopefully someone can point out what I'm missing here.
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
gem "rails", "~> 5.2.0"
gem "pg", "~> 0.12"
gem "mobility", "~> 1.2.9"
gem "i18n", "< 1.9.0"
end
require "active_record"
require "minitest/autorun"
require "mobility"
Mobility.configure do
plugins do
backend :key_value
active_record
reader
writer
column_fallback true
fallbacks({ :en => :de }) # this is the line that's giving me headaches
locale_accessors [:en, :de]
end
end
I18n.available_locales = [:en, :de]
I18n.default_locale = :en
ActiveRecord::Base.establish_connection(adapter: "postgresql", host: "localhost", database: "testing", user: "postgres", password: "password")
ActiveRecord::Schema.define do
#enable_extension "plpgsql"
create_table "mobility_string_translations", force: :cascade do |t|
t.string "locale", null: false
t.string "key", null: false
t.string "value"
t.string "translatable_type"
t.bigint "translatable_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["translatable_id", "translatable_type", "key"], name: "index_mobility_string_translations_on_translatable_attribute"
t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_string_translations_on_keys", unique: true
t.index ["translatable_type", "key", "value", "locale"], name: "index_mobility_string_translations_on_query_keys"
end
create_table "mobility_text_translations", force: :cascade do |t|
t.string "locale", null: false
t.string "key", null: false
t.text "value"
t.string "translatable_type"
t.bigint "translatable_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["translatable_id", "translatable_type", "key"], name: "index_mobility_text_translations_on_translatable_attribute"
t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_text_translations_on_keys", unique: true
end
create_table "contents", id: :serial, force: :cascade do |t|
t.string "key"
t.text "body"
end
end
class Content < ActiveRecord::Base
self.table_name = "contents"
extend Mobility
translates :key, type: :string
translates :body, type: :text
end
class BugTest < ActiveSupport::TestCase
test "should have fallbacks enabled" do
model = Content.create!
model.key = "foo"
model.body = "bar"
model.save
model.reload
assert model.key # works as expected
assert model.body # works as expected
assert model.key(locale: :de) # fails. expecting "foo"
assert model.body(locale: :de) # fails. expecting "bar"
assert model.key_de # fails
assert model.body_de # fails
end
end
Related
I am trying to write and learn about test cases in ruby on rails. My sites controller has authenticate_user! before_action hook in my SitesController.
I have Devise gem already installed in the application through which I authenticate users normally in my application. I also have devise_invitable and simple_token_authentication(for authentication in api calls) gems.
Here is what my concerned files look like
app/models/user.rb
class User < ApplicationRecord
acts_as_token_authenticatable
default_scope { order(email: :asc) }
# Include default devise modules. Others available are:
# , :lockable, , and :omniauthable
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable, :trackable, :timeoutable, :lockable
include UserStudies
include UserAgreements
has_person_name
# Validations
validates :name, presence: true
before_destroy :check_for_studies
end
config/environments/test.rb
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
config.cache_classes = false
config.action_view.cache_template_loading = true
# Do not eager load code on boot. This avoids loading your whole application
# just for the purpose of running a single test. If you are using a tool that
# preloads Rails for running tests, you may have to set it to true.
config.eager_load = false
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{1.hour.to_i}"
}
routes.default_url_options = {host: "localhost", port: 5000}
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.cache_store = :null_store
# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
config.action_mailer.perform_caching = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
# Raises error for missing translations.
# config.action_view.raise_on_missing_translations = true
end
test/test_helper.rb
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
class ActiveSupport::TestCase
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors)
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
include Devise::Test::IntegrationHelpers
# Add more helper methods to be used by all tests here...
end
test/controllers/sites_controller_test.rb
require 'test_helper'
class SitesControllerTest < ActionDispatch::IntegrationTest
setup do
puts users(:one).inspect
sign_in users(:one)
#site = sites(:one)
end
test "should get index" do
get sites_url
assert_response :success
end
end
test/fixtures/users.yml
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the '{}' from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
one:
email: umair.one#gmail.com
first_name: Test
last_name: one
encrypted_password: <%= Devise::Encryptor.digest(User, '123456') %>
# column: value
#
two:
email: umair.two#gmail.com
first_name: Test
last_name: two
encrypted_password: <%= Devise::Encryptor.digest(User, '123456') %>
# column: value
test/fixtures/sites.yml
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
code: MyString
name: MyString
street_address: MyString
city: MyString
state: MyString
zip_code: MyString
phone: MyString
two:
code: MyString
name: MyString
street_address: MyString
city: MyString
state: MyString
zip_code: MyString
phone: MyString
db/schema.rb
create_table "sites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "code"
t.string "name"
t.string "street_address"
t.string "city"
t.string "state"
t.string "zip_code"
t.string "country"
t.string "phone"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.string "first_name"
t.string "last_name"
t.string "time_zone"
t.datetime "accepted_terms_at"
t.datetime "accepted_privacy_at"
t.datetime "announcements_read_at"
t.boolean "admin"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "invitation_token"
t.datetime "invitation_created_at"
t.datetime "invitation_sent_at"
t.datetime "invitation_accepted_at"
t.integer "invitation_limit"
t.string "invited_by_type"
t.uuid "invited_by_id"
t.integer "invitations_count", default: 0
t.datetime "locked_at"
t.string "authentication_token", limit: 30
t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
t.index ["invitations_count"], name: "index_users_on_invitations_count"
t.index ["invited_by_id"], name: "index_users_on_invited_by_id"
t.index ["invited_by_type", "invited_by_id"], name: "index_users_on_invited_by_type_and_invited_by_id"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
When I run my test case despite having sign_up user inside the test controller I am getting this error.
Failure:
SitesControllerTest#test_should_get_index [/home/app/test/controllers/sites_controller_test.rb:13]:
Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://localhost:5000/users/sign_in>
Response body: <html><body>You are being redirected.</body></html>
I have followed GoRails tutorial to setup devise authentication in test controllers. What am I doing wrong?
Solved this by adding confirmed_at and confirmation_sent_at to my fixture. My fixture looks like this:
admin:
email: 'admin#example.com'
encrypted_password: <%= Devise::Encryptor.digest(User, '<PASSWORD>') %>
confirmed_at: <%= Time.zone.now - 1.hour %>
confirmation_sent_at: <%= Time.zone.now - 2.hours %>
admin: true
I have used 4 important gems
gem 'activeadmin'
gem 'devise'
gem 'cancancan', '~> 2.0'
gem 'friendly_id', '~> 5.2.4'
I have 2 main Controller "User" and "Post" and registered in active_admin(rails generate active_admin:resource User and rails generate active_admin:resource Post).I have used cancancan for the authorization.
I have defined the ability in
ability.rb
if user.admin?
can :manage, :all
else
can :read, Posts
end
ApplicationController.rb
class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.json { head :forbidden, content_type: 'text/html' }
format.html { redirect_to main_app.root_url, notice: exception.message }
format.js { head :forbidden, content_type: 'text/html' }
end
end
def current_ability
#current_ability ||= AccountAbility.new(current_admin_user)
end
end
schema.rb
ActiveRecord::Schema.define(version: 2019_02_14_200911) do
create_table "active_admin_comments", force: :cascade do |t|
t.string "namespace"
t.text "body"
t.string "resource_type"
t.integer "resource_id"
t.string "author_type"
t.integer "author_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id"
t.index ["namespace"], name: "index_active_admin_comments_on_namespace"
t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id"
end
create_table "admin_users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "admin", default: true
t.index ["email"], name: "index_admin_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
end
create_table "categories", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "published_at"
t.integer "user_id"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.index ["category_id"], name: "index_posts_on_category_id"
t.index ["slug"], name: "index_posts_on_slug", unique: true
t.index ["user_id"], name: "index_posts_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.boolean "admin", default: false
t.index ["slug"], name: "index_users_on_slug", unique: true
end
end
Goal
My goal is: 1. Only Admin can post,add user, add category basically manage all.
2. guest User can only read.
So I used cancancan gem to do authorization. But I am getting error:
uninitialized constant ApplicationController::AccountAbility
Extracted source (around line #12):
end
def current_ability
#current_ability ||= AccountAbility.new(current_admin_user)
end
end
And I have uploaded on git if you need any further information.
It will be great if you can help me. Thanks in advance.
The NameError is because your model is named Ability, not AccountAbility, so change your current_ability method to:
def current_ability
#current_ability ||= Ability.new(current_admin_user)
end
I'm trying to test the User Model, which I've got Devise authentication.
But I've got a failure:
Failure:
UserTest#test_should_be_valid [/home/nikki/Documents/TheHackingProject/week06/monday/the_private_club/test/models/user_test.rb:12]:
Expected false to be truthy.
This is my user_test.rb file:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
#user = User.create(
first_name: 'Marge',
last_name: 'Simpson',
email: 'marge#simpson.com'
)
end
test 'should be valid' do
assert #user.valid?
end
test 'first name cant be blank' do
#user.first_name = ""
assert_not #user.valid?
end
test 'last name cant be blank' do
#user.last_name = ""
assert_not #user.valid?
end
end
And this my schema.rb file:
ActiveRecord::Schema.define(version: 2018_11_05_115404) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "first_name"
t.string "last_name"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end
Also, I don't understand how can I test the password which is encrypted?
I am implementing an authentication system with devise in rails, but when registering the user, in this case a student, the following error is skipped:
ActiveRecord::NotNullViolation in Devise::RegistrationsController#create
SQLite3::ConstraintException: NOT NULL constraint failed: students.codigo: INSERT INTO "students" ("email", "encrypted_password", "created_at", "updated_at") VALUES (?, ?, ?, ?)
I have been solving this error all day, but nothing that I can solve. I have implemented a before_action in the students_controller controller:
class StudentsController < ApplicationController
before_action :configure_devise_params, if: :devise_controller?
def configure_devise_params
devise_parameter_sanitizer.permit(:sign_up) do |user|
user.permit(:codigo, :documento, :nombres, :apellidos, :es_egresado, :email, :password, :password_confirmation)
end
end
...
end
but still the problem is not solved.
The same error happens when I uninstall devise, remove all reference from it, and also, implement strong params in the controller, without this the registry works satisfactorily. But with strong params and devise does not work, it passes data to null in the SQL command.
The student database is the following:
create_table "students", id: false, force: :cascade do |t|
t.integer "codigo", null: false
t.integer "documento", null: false
t.string "nombres", null: false
t.string "apellidos", null: false
t.integer "es_egresado", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.decimal "promedio_carrera"
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["documento"], name: "index_students_on_documento", unique: true
t.index ["email"], name: "index_students_on_email", unique: true
t.index ["reset_password_token"], name: "index_students_on_reset_password_token", unique: true
end
And its implementation migrate is the following:
class DeviseCreateStudents < ActiveRecord::Migration[5.1]
def change
create_table :students, {
:id => false,
:primary_key => :codigo
} do |t|
## Database authenticatable
t.integer :codigo, null: false
t.integer :documento, null: false
t.string :nombres, null: false
t.string :apellidos, null: false
t.integer :es_egresado, null: false
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
t.decimal :promedio_carrera, null: true
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :students, :email, unique: true
add_index :students, :reset_password_token, unique: true
add_index :students, :documento, unique: true
# add_index :students, :confirmation_token, unique: true
# add_index :students, :unlock_token, unique: true
end
end
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"6zdENP1iREYYM+qpqtskqRJ+aB38NIX/nG9jFsoGlImqUVyS9bsmBF6Uc7xvDD/J50/zamlZbbm2rwAaCgOmuw==",
"student"=>
{"codigo"=>"625762",
"documento"=>"107526792",
"nombres"=>"Carlos",
"apellidos"=>"Garnica",
"es_egresado"=>"0",
"email"=>"wcarlosfg.1234567890#hotmail.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"},
"commit"=>"Registrar"}
I also clarify that when I create another Student table, removed the previous one, and I remove the null option to the fields, the error does not appear, but the fields that supposedly have been saved remain in NULL in the database
UPDATE: Modify the database, making all fields null, and the form sends null fields whether they have data in the form or not, it is very rare. When doing inspect on the object, it shows the following:
irb(main):002:0> Student.last
Student Load (0.0ms) SELECT "students".* FROM "students" ORDER BY "students"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<Student id: 2, codigo: nil, documento: nil, nombres: nil, apellidos: nil, es_egresado: nil, promedio_carrera: nil, email: "wcarlosfg.1234567890#hotmail.com", created_at: "2018-09-30 07:46:39", updated_at: "2018-09-30 07:46:39">
SOLUTION: After a day looking at the error, the only solution was to overwrite the devise driver for records, with this, I was able to add the logic and implement strong params to make the registry work correctly.
I have a table mobility_string_translations, but I do not understand how to get to it.
Now I have three records. Two records in German and one in Spanish. I want to get only those records that are in German.
this not working:
all_de_posts = Post.i18n.find_by(locale: :de)
First of all you may have a column.(Lets say title.)
On your model post.rb you will have something like this:
class Post < ApplicationRecord
extend Mobility
translates :title, type: :string, locale_accessors: [:en, :de]
end
The type is important in order to get the :de records(or the :en).
At schema.rb wou will see a table mobility_string_translations
create_table "mobility_string_translations", force: :cascade do |t|
t.string "locale", null: false
t.string "key", null: false
t.string "value"
t.integer "translatable_id", null: false
t.string "translatable_type", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["translatable_id", "translatable_type", "key"], name: "index_mobility_string_translations_on_translatable_attribute"
t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_string_translations_on_keys", unique: true
t.index ["translatable_type", "key", "value", "locale"], name: "index_mobility_string_translations_on_query_keys"
end
Well now at your console find your records with locale: :de
irb:> Post.all
irb:> Mobility::ActiveRecord::StringTranslation.where(locale: :de)
As you can see at your schema.rb --> "mobility_string_translations" you can play around with your columns in order to find exactly what you want.