Any chance of expanding data types for mobility - mobility

I love the gem and how it works, I was just wondering if there was any existing or planned functionality to specify data types other than text and string for the translations (stored in mobility_[type]_translations)?

This is not documented, but it's not hard to support other types like Integer, Float, etc.
e.g. for Integer, you'd have to create a table like this:
create_table "mobility_integer_translations", force: :cascade do |t|
t.string "locale", null: false
t.string "key", null: false
t.integer "value"
t.string "translatable_type"
t.bigint "translatable_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, 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
then you'd need to create a class for this table:
module Mobility
module Backends
class ActiveRecord::KeyValue
class IntegerTranslation < Translation
self.table_name = "mobility_integer_translations"
end
end
end
end
I believe this should be enough, and I think you should be able to just do this (assuming your config has key_value as the backend):
translates :foo, type: :integer
There's nothing in Mobility itself that actually says you can't use another translation class, it's just that these are not offered out-of-the-box. Probably this should be added to the Wiki somewhere.

Related

How to simplify my Active Record code (I want to filter has_many using join table column)?

This is my schema
create_table "duties", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.boolean "is_general", default: false
t.boolean "write_all", default: true
end
create_table "duties_users", force: :cascade do |t|
t.bigint "duty_id"
t.bigint "user_id"
t.boolean "has_write_access", default: true
t.index ["duty_id"], name: "index_duties_users_on_duty_id"
t.index ["user_id"], name: "index_duties_users_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "mail"
t.text "password_digest"
t.datetime "birth_date"
t.boolean "is_admin", default: false
t.integer "rating", default: 0
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "name", default: ""
t.string "surname", default: ""
t.string "patronymic", default: ""
t.datetime "restore_date", default: "2021-02-11 09:57:14"
t.boolean "is_boss", default: false
end
My join model
class DutyUser < ApplicationRecord
self.table_name = "duties_users"
belongs_to :user
belongs_to :duty
end
User model
class User < ApplicationRecord
has_secure_password
has_many :duties_users, class_name: "DutyUser"
has_many :duties, through: :duties_users, class_name: "Duty"
end
And duty model
class Duty < ApplicationRecord
has_many :duties_users, class_name: "DutyUser"
has_many :users, through: :duties_users, class_name: "User"
end
I want to find all users who have write acces to a specific duty. In order to achive it i need to use something like this:
Duty.first.duties_users.find_all{|m| m.has_write_access}.map{|m| m.user}
How can i simplify this line of code?
The key points here are:
Duty has_many users. So if you have a duty, you can always do duty.users and that will give you the users in what's known as an "ActiveRecord Relation"
ActiveRecord Relations act a lot like arrays, except that they only give you access to instances of one model (in this case, duty.users will return a relation full of User instances) which allow you to run further queries, as well as any class methods on User. Note that unlike an array, a Relation doesn't actually contain anything. It quietly builds some SQL, and only sends that SQL to the database when you force it to (e.g. by iterating over it with .each, or asking for a .count)
Queries can be done using the where method - e.g. .where(has_write_access: true) - this is done in the database, so it's super quick.

How to convert SQL into activerecord Rails

My sql that gives me the results I want (a single record from books table).
select *
from books
where book_number = ? and id not in (select book_id from checkout_logs where returned_date is null) limit 1
my best active record attempt:
#book = Book.where(book_number: params[:book_number]).where.not(id: CheckoutLog.where(returned_date: nil)).find(1)
? is the params[:book_number]
here is my Rails schema.rb
ActiveRecord::Schema.define(version: 2020_12_30_171415) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "books", force: :cascade do |t|
t.string "title"
t.string "author"
t.string "genre"
t.string "subgenre"
t.integer "pages"
t.string "publisher"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "book_number"
end
create_table "checkout_logs", force: :cascade do |t|
t.datetime "checkout_date"
t.datetime "due_date"
t.datetime "returned_date"
t.bigint "user_id", null: false
t.bigint "book_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["book_id"], name: "index_checkout_logs_on_book_id"
t.index ["user_id"], name: "index_checkout_logs_on_user_id"
end
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", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.boolean "admin", default: false
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
add_foreign_key "checkout_logs", "books"
add_foreign_key "checkout_logs", "users"
end
The idea is I want to get a single book (id) from the book table with a given book_number (think ISBN) that doesn't have a record in checkout_log table with a matching book_id and a null for returned_date. (indicating a book is checked out and not available.)
EDIT: changed the last part from .take to .find(1) which now does take 1 record, problem is the
.where.not(id: CheckoutLog.where(returned_date: nil))
part isn't filtering out the books which have a checkout_log record and no returned_date.
.where.not(id: CheckoutLog.where(returned_date: nil))
=> I think you are filtering wrong id, you need to filter by book's id, not id of CheckoutLog.
So your query should be:
.where.not(id: CheckoutLog.where(returned_date: nil).pluck(:book_id))
Use of the includes query method may help.
Book.includes(:checkout_logs).where("books.book_number=? AND checkout_logs.returned_date IS NOT NULL", params[:book_number]).limit(1)
More info on query methods is in the api

How to Sort By Association Attribute Value in Rails

I'm sure I'm missing something basic, but I have a model User which belongs_to a Company model which has a name attribute.
How can I sort all users by their Company name?
#users = User.all #sort this collection in order of the associated company's name
class User < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
end
create_table "users", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "company_id"
end
create_table "companies", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
User.includes(:company).order('company.name') . I believe ASC is default, but you can add it if necessary. You may also have to specify the Company table name in the model, or change the table name to 'companies', as that is the default pluralization convention over configuration will assume I believe.

Refactoring composite primary key to simple key for Rails?

My application uses Ruby on Rails ActiveRecord models, which don't allow for composite keys without installing a third-party gem such as composite-primary-keys. Is there a way I can refactor a composite key into a simple key so it will fit this paradigm, or should I bite the bullet and install the gem?
I'm still at the early design stage so I have no data I need to worry about, and I'd like to stay as true to Rails idioms as possible.
I'm creating a recipe database that can list ingredients and instructions in a step-by-step manner. The database schema is similar to the one shown below, and is using composite keys in the Recipe_Steps and Recipe_Step_Ingredients tables (bottom center of image).
You're very likely overthinking and overcomplicating the problem. Stick with the AR idiom of using a single primary key named id.
For join tables use foreign keys instead of compound PKs. Also stick to the naming conventions unless you want to look inept or annoy other devs by violating the principle of least surprise. That means:
use snake_case for everything (table names, columns, index names etc.)
don't prefix the column with the table name. It just makes every variable in your application longer and is not needed in a ORM.
use _id for foreign key columns. ex; parent_id
use _at for timestamps. ex; confirmed_at
use thing_other_things for join tables unless there is a more descriptive name
Also many of these cases should just use an indirect relation to join up the hierarchy instead of duplicating foreign keys.
This is an example DB schema:
ActiveRecord::Schema.define(version: 20161214013752) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "ingredient_types", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "ingredients", force: :cascade do |t|
t.integer "ingredient_type_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["ingredient_type_id"], name: "index_ingredients_on_ingredient_type_id", using: :btree
end
create_table "recipe_ingredients", force: :cascade do |t|
t.integer "recipe_id"
t.integer "ingredient_id"
t.float "quantity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["ingredient_id"], name: "index_recipe_ingredients_on_ingredient_id", using: :btree
t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id", using: :btree
end
create_table "steps", force: :cascade do |t|
t.integer "recipe_id"
t.integer "ordinal"
t.text "instruction"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["recipe_id"], name: "index_steps_on_recipe_id", using: :btree
end
create_table "recipes", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "ingredients", "ingredient_types"
add_foreign_key "recipe_ingredients", "ingredients"
add_foreign_key "recipe_ingredients", "recipes"
add_foreign_key "steps", "recipes"
end
class IngredientType < ApplicationRecord
has_many :ingredients
end
class Ingredient < ApplicationRecord
belongs_to :ingredient_type
has_many :recipe_ingredients
has_many :recipes, through: :recipe_ingredients
end
class RecipeIngredient < ApplicationRecord
belongs_to :recipe
belongs_to :ingredient
has_one :ingredient_type, through: :ingredient
end
class Step < ApplicationRecord
belongs_to :recipe
end
class Recipe < ApplicationRecord
has_many :recipe_ingredients
has_many :ingredients, through: :recipe_ingredients
has_many :steps
end

How to import data into rails models from a table in a database

I have the following schema in my rails app:
ActiveRecord::Schema.define(version: 20161020060112) do
create_table "authors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "languages", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "quotes", force: :cascade do |t|
t.string "title"
t.date "date"
t.string "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "author_id"
t.integer "language_id"
t.integer "source_id"
t.index ["author_id"], name: "index_quotes_on_author_id"
t.index ["language_id"], name: "index_quotes_on_language_id"
t.index ["source_id"], name: "index_quotes_on_source_id"
end
create_table "source_types", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sources", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "source_type_id"
t.index ["source_type_id"], name: "index_sources_on_source_type_id"
end
end
And this is the schema of the table that I want to import data from:
CREATE TABLE quotation_data(
id integer primary key,
author_name text,
source_type text,
source text,
language text,
date text,
title text,
body text
);
I did find some posts on Stackoverflow but none that tell about importing data from a single table into fields distributed over many models.
Thanks!
I have imported data from database I create the list that list everything that I have inserted in the database.
like:
Under controller#show
def show
#quotes = Quote.all
end
views#show
<ul>
<% #quotes.each do |quote| %>
<li>
<%= link_to quotes.title, edit_quotes_path(quote) %>
</li>
<% end %>
</ul>
This will help you to view what is on the database and edit it also.