Phoenix/Elixir - timestamps does not exist, expanding to timestamps() - postgresql

I am learning Phoenix using the book 'Programming Phoenix'. The first project has creating a postgres db and this is our migration. I cannot get rid of a warning for timestamps in my schema.
defmodule Rumbl.Repo.Migrations.CreateUser do
use Ecto.Migration
def change do
create table(:users) do
add :name, :string
add :username, :string, null: false
add :password_hash, :string
timestamps
end
create unique_index(:users, [:username])
end
end
Then our model corresponding to this migration is:
defmodule Rumbl.User do
use Rumbl.Web, :model
schema "users" do
field :name, :string
field :username, :string
field :password, :string, virtual: true
field :password_hash, :string
timestamps
end
end
Now I run the migration, followed by mix phoenix.server.
And I get this warning:
warning: variable "timestamps" does not exist and is being expanded to "timestamps()",
please use parentheses to remove the ambiguity or change the variable name
web/models/user.ex:10
If I change timestamps in the schema to timestamps() it doesn't complain, anymore, but the book never shows what the model's schema looks like after running the migration. Is that supposed to be right, or is there something else that fixes that? What is the 'timestamps' representation in an Ecto/Phoenix schema supposed to look like?

Elixir 1.4 added a warning on calling an imported or locally defined function with 0 arguments without a parentheses, as it is ambiguous on what it should mean when you have a local variable with the same name as the function and you write the variable name without parentheses.
[Kernel] Warn if variable is used as a function call
Source
The book probably hasn't been updated for Elixir 1.4 yet. Phoenix generators were updated to add the parentheses in June 2016.

Related

Changing a field type with a migration

I have an Info table with a age field which is an int
But I want to change the type to a date so that I can calculate the age dynamically.
So I did this migration :
class ChangeDateFormatInInfo < ActiveRecord::Migration[5.0]
def change
change_column :infos, :age, :date
end
end
But there was a DatatypeMismatch
column "age" cannot be cast automatically to type date
So I changed it to :
change_column :infos, :age, :date, 'USING age::date'
But it still gives me
migrateTypeError: no implicit conversion of Symbol into Integer
I don't really get it, I'm telling it to convert it, so what am I doing wrong ?
You have to first remove the column and then add it again with new datatype.
So try below code:
rails g migration ChangeDateFormatInInfo
then goto db/migrate/change_date_format_in_info_xxxx.rb file and open it.
Add below code in migration file:
class ChangeDateFormatInInfo < ActiveRecord::Migration[5.0]
def change
remove_column :infos, :age
add_column :infos, :age, :date
end
end
then run command:
rake db:migrate
Didn't thought about it, I just did a migration to drop the field, and another one to add a field. Done now :)
rails g migration remove_age_from_info age:integer
rails g migration add_age_to_info age:date

belong_to association and ActiveRecord::InvalidForeignKey in Rails 5

My application has two models: Service and User. A service may have and assigned driver or not. I have implemented this as:
class User < ApplicationRecord
end
and:
class Service < ApplicationRecord
has_and_belongs_to_many :users
belongs_to :driver, class_name: "User", optional: true
end
Note that since a particular service may or may not have a driver, I have marked the association as optional. And I don't have any pointer from the User model to Service.
I have the following migration implementing this association:
class AddOPtionalDriverToService < ActiveRecord::Migration[5.0]
def change
add_reference :services, :driver, references: :users, index: true
add_foreign_key :services, :users, column: :driver_id
end
end
The relevant part of my schema when I run the migration is:
create_table "services", force: :cascade do |t|
t.string "destination"
....
t.text "comments"
t.index ["driver_id"], name: "index_tdy_requests_on_driver_id", using: :btree
end
...
add_foreign_key "tdy_requests", "users", column: "driver_id"
end
My problem is that when I try to create a new service without a driver my params contains a value of "0" for the driver:
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"kVC53huZYZxuF4akSiqkGkSvoo5p2f4dQ==",
"service"=>{
"destination"=>"Some where", ... ,
"driver_id"=>"0",
"comments"=>""},
"commit"=>"Create Service"}
But since driver_id is "0" I get the following exception:
PG::ForeignKeyViolation: ERROR: insert or update on
table "services" violates foreign key constraint
"fk_rails_15497e1c36" DETAIL: Key (driver_id)=(0) is
not present in table "users"
This makes lots of sense but the funny thing is that I have discovered this when I move from SQLite to PostgreSQL because it was working fine with SQLite. At least the application was doing what I wanted to do. I'm using Rail 5.0.2.
I would like to know how can modify my models or migrations to avoid this exception. Any ideas?
Thank you very much in advance.
I'm sorry. I have just realized the solution. Something that I didn't say was that I enter the driver using a select element. Since the driver is optional I was adding and "empty" using:
<option value="0"></option>
changing to:
<option value=""></option>
solves the problem.
I'd prefer to remove the question since doesn't offer any value.

Though I have a record in database, it's giving "Mongoid::Errors::DocumentNotFound"

Though I have the record with id 13163 (db.locations.find({_id: 13163})), it's giving me error:
Mongoid::Errors::DocumentNotFound in LocationsController#show
Problem: Document(s) not found for class Location with id(s) 13163.
Summary: When calling Location.find with an id or array of ids, each
parameter must match a document in the database or this error will be
raised. The search was for the id(s): 13163 ... (1 total) and the
following ids were not found: 13163. Resolution: Search for an id that
is in the database or set the Mongoid.raise_not_found_error
configuration option to false, which will cause a nil to be returned
instead of raising this error when searching for a single id, or only
the matched documents when searching for multiples.
# Use callbacks to share common setup or constraints between actions.
def set_location
#location = Location.find(params[:id])
end
locations_controller.rb:
class LocationsController < ApplicationController
before_action :set_location, only: [:show, :edit, :update, :destroy]
# GET /locations
# GET /locations.json
def index
#locations = Location.all
end
# GET /locations/1
# GET /locations/1.json
def show
end
private
# Use callbacks to share common setup or constraints between actions.
def set_location
#location = Location.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def location_params
params.require(:location).permit(:loc_name_en, :loc_name_jp, :channel)
end
end
Setting up the option raise_not_found_error: false is not the case as I do have a document in database.
SOLUTION:
Big thanks to #mu is too short for giving me a hint.
The problem can be solved in 2 ways:
Declare field :_id, type: Integer in the model location.rb
Or converting the passing parameter to Integer like Location.find(params[:id].to_i) in locations_controller.rb as shown below in the #mu is too short's answer
I'd guess that you have a type problem. You say that this:
db.locations.find({_id: 13163})
finds the document in the MongoDB shell. That means that you have a document in the locations collection whose _id is the number 13163. If you used the string '13163':
db.locations.find({_id: '13163'})
you won't find your document. The value in params[:id] is probably a string so you're saying:
Location.find('13163')
when you want to say:
Location.find(13163)
If the _id really is a number then you'll need to make sure you call find with a number:
Location.find(params[:id].to_i)
You're probably being confused because sometimes Mongoid will convert between Strings and Moped::BSON::ObjectIds (and sometimes it won't) so if your _id is the usual ObjectId you can say:
Model.find('5016cd8b30f1b95cb300004d')
and Mongoid will convert that string to an ObjectId for you. Mongoid won't convert a String to a number for you, you have to do that yourself.

Mongoid and collections

I am trying to configure and use mongoid for the first time. I have set the mongoid.yml config file simply as:
host: localhost
database: table
and my code:
Mongoid.load!("/mongoid.yml")
class Data
include Mongoid::Document
field :study, type: String
field :nbc_id, type: String
field :short_title, type: String
field :source, type: String
field :start_date, type: Date
end
puts Data.study
I keep getting an error:
NoMethodError at / undefined method `study' for Data:Class
I think it is because I have not specified the collection name which is 'test'. However I can find no examples on how to do this. Do I specify it in the .yml file or in the code. What is the correct syntax. Can anyone point me in the right direction?
Tx.
According to the Mongoid documentation, "Mongoid by default stores documents in a collection that is the pluralized form of the class name. For the following Person class, the collection the document would get stored in would be named people."
http://mongoid.org/docs/documents.html
The documentation goes on to state that Mongoid uses a method called ActiveSupport::Inflector#classify to determine collection names, and provides instructions on how to specify the plural yourself.
Alternatively, you can specify the collection name in your class by including "store_in" in your class definition.
class Data
include Mongoid::Document
store_in :test
Hope this helps!

using mongoid on sinatra , model inheritance causing "uninitialized constant" modelname (parent class)

I'm using Mongoid on Sinatra . And I use
Dir.glob(File.join(File.dirname(__FILE__),'models','*.rb')).each do |file|
require file
end
to load mongoid model files.
I tried to add a model B inherit from A ,like:
models/a.rb:
class A
include Mongoid::Document
include Mongoid::Timestamps
field :custom_id, type: Integer
end
models/b.rb
class B < A
field :title , type: String
field :body , type: String
end
But when I exec the App , I got errors :
uninitialized constant A (NameError)
So I'm trying to find a solution for this,
it could be fixed by add:
require A
at the top of model B , But I think it may not be a good way to solve it.
So , is there anyother way to fix this??
Regards
You just require your file where you A class is define.
Sinatra has no auto_load system like rails have. So you need doing all of your require needed.
require 'a'
class B < A
field :title , type: String
field :body , type: String
end