According to http://elixir-lang.org/getting-started/alias-require-and-import.html#aliases
I should be able to have this code working:
defmodule A do
alias A.B, as: C
defmodule B do
defstruct name: ""
end
end
iex(1)> %C{}
But instead i'm having this error:
** (CompileError) iex:1: C.__struct__/0 is undefined, cannot expand struct C
Any idea of what i'm missing here ?
Edit: Module naming is simplified here for the exemple
This works only for the module in which the alias is defined, e.g.:
defmodule A do
alias A.B, as: C
defmodule B do
defstruct name: ""
end
def new do
%C{}
end
end
You could then do:
iex(6)> A.new
%A.B{name: ""}
This will also work in iex if you type the alias there:
iex(7)> alias A.B, as: C
nil
iex(8)> %C{}
%A.B{name: ""}
Related
I am new to this amber framework,crystal lang and object oriented programming in general . I was following the tutorials and tried to create a simple form using this scaffold
amber g scaffold item name:string path:string type:string size:float created:date
I can see that this class has been created on the models folder
class Item < Granite::Base
connection pg
table items
column id : Int64, primary: true
column name : String?
column path : String?
column type : String?
column size : Float64?
column created : Time?
timestamps
end
When i launched the app and tired to insert a new item i got this error
Created Expected created to be (Time | Nil) but got (Float64 |
String).
This is the code of the .slang form
== form(action: "/items/#{item.id.to_s}", method: item.id ? :patch : :post) do
== csrf_tag
.form-group
== text_field(name: "name", value: item.name, placeholder: "Name", class: "form-control")
.form-group
== text_field(name: "path", value: item.path, placeholder: "Path", class: "form-control")
.form-group
== text_field(name: "type", value: item.type, placeholder: "Type", class: "form-control")
.form-group
== text_field(name: "size", value: item.size, placeholder: "Size", class: "form-control")
.form-group
== text_field(name: "created", value: item.created, placeholder: "Created", class: "form-control")
== submit("Submit", class: "btn btn-success btn-sm")
== link_to("Back", "/items", class: "btn btn-light btn-sm")
I am guessing that when i input a value like 2020-01-01 00:01:00 this is handled as a string but i need this to be converted to a Time type. I presume this needs to happen on the related controller file, but i don't know how to do this.
This is the code that gets executed when i try to create a new item.
def create
item = Item.new item_params.validate!
if item.save
redirect_to action: :index, flash: {"success" => "Item has been created."}
else
flash[:danger] = "Could not create Item!"
render "new.slang"
end
end
thanks,
gurrurin
I was able to make thsi work by creating some new fucntions
class Item < Granite::Base
...
def set_id (id : Int64)
#id = id
end
def set_name (name : String)
#name = name
end
def set_filetype (filetype : String)
#filetype = filetype
end
def set_filesize (filesize : Float64)
#filesize = filesize
end
def set_filepath (filepath : String)
#filepath = filepath
end
def set_created (cr : Time)
#created = cr
end
and then used those after inilializing an empty item
item = Item.new
item.set_name(params[:name])
item.set_filetype(params[:filetype])
item.set_filesize(params[:filesize].to_f64)
item.set_filepath(params[:filepath])
item.set_created(Time.parse(params[:created],"%Y-%m-%d %H:%M:%S %z", Time::Location::UTC))
though i dont think this is the best solution here .
Since this parmas object stores everything as strings i suspect that when you initialize a new item with this the a string like ' 2020-01-01 00:01:00 UTC' is not converter to the apropriate Time type. Other types like Int or Float seem to work fine with no isseus.
item = Item.new item_params.validate!
You don't need any of those extra setter methods, they are already defined by the column macro. You don't need an extra created column, there are already created_at and updated_at columns created by the timestamps macro and they are updated for you when creating a new record and saving a modified record.
Other than that if you DID create a column that was a Time type you would do
item.created = Time.parse(params[:created],"%Y-%m-%d %H:%M:%S %z", Time::Location::UTC)
and Granite will do the right conversion for each database engine when you save and read the record.
I'm working with Ecto (with Postgres) for the first time and I have the following two schemas (both somewhat simplified):
defmodule RailroadServer.Database.RailroadSystem do
#moduledoc """
Schema for an entire railroad system.
"""
use Ecto.Schema
import Ecto.Changeset
alias RailroadServer.Database
schema "railroad_systems" do
field :railroad_system_name, :string
has_many :depos, Database.Depo
end
#fields ~w(railroad_system_name)a
def changeset(data, params \\ %{}) do
data
|> cast(params, #fields)
|> validate_required([:railroad_system_name])
|> validate_length(:railroad_system_name, max: 50)
end
end
defmodule RailroadServer.Database.Depo do
#moduledoc """
Schema for a node that stores trains.
"""
use Ecto.Schema
import Ecto.Changeset
alias RailroadServer.Database
schema "depos" do
field :capacity, :integer
field :depo_uuid, :string
field :depo_name, :string
belongs_to :railroad_system, Database.RailroadSystem
end
#fields ~w(capacity depo_uuid depo_name)a
def changeset(data, params \\ %{}) do
data
|> cast(params, #fields)
|> validate_required([:capacity, :depo_uuid, :depo_name])
|> validate_number(:capacity, greater_than: 0)
|> validate_length(:depo_name, max: 50)
|> validate_length(:depo_uuid, max: 50)
|> foreign_key_constraint(:railroad_system_id)
end
end
Based on these migrations:
defmodule RailroadServer.Database.Repo.Migrations.CreateRailroadSystems do
use Ecto.Migration
def change do
create table(:railroad_systems) do
add :railroad_system_name, :varchar, null: false, size: 50
end
create unique_index("railroad_systems", [:railroad_system_name])
end
end
defmodule RailroadServer.Database.Repo.Migrations.CreateDepos do
use Ecto.Migration
def change do
create table(:depos) do
add :railroad_system_id, references("railroad_systems"), null: false
add :depo_uuid, :varchar, size: 50, null: false
add :depo_name, :varchar, size: 50, null: false
add :capacity, :integer, null: false
end
create index("depos", [:railroad_system_id])
create index("depos", [:depo_uuid], unique: true)
create index("depos", [:depo_name], unique: true)
end
end
Which I'm constructing with the following code:
def insert_railway_system(system_name, depos) do
cs = %RailroadSystem{}
|> RailroadSystem.changeset(%{railroad_system_name: system_name})
|> put_assoc(:depos, create_depos(depos))
if cs.valid? do
Repo.insert(cs)
else
{:error, cs}
end
end
_ = """
Uses a list of depo nodes to construct a list of depo changeset.
"""
defp create_depos(depos) do
Enum.map(depos, fn(depo) -> Depo.changeset(%Depo{}, depo) end)
end
However, when I run this function (with data that produces a valid changeset), I get a NULL column error because the foreign key for the railway system in the depo struct doesn't exist. How do I make sure that Ecto passes that foreign key?
The output:
19:06:07.401 [debug] QUERY OK db=0.8ms
begin []
19:06:07.406 [debug] QUERY OK db=0.6ms
INSERT INTO "railroad_systems" ("railroad_system_name") VALUES ($1) RETURNING "id" ["test Can insert railway system"]
19:06:07.409 [debug] QUERY ERROR db=2.7ms
INSERT INTO "depos" ("capacity","depo_name","depo_uuid") VALUES ($1,$2,$3) RETURNING "id" [23, "A depo", "d387a91b-db77-4758-87ed-9951d5c2de8a"]
19:06:07.410 [debug] QUERY OK db=0.1ms
rollback []
1) test Can insert railway system (RailroadServer.DatabaseTest)
apps/railroad_server/test/railroad_server/database_test.exs:9
** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "railroad_system_id" violates not-null constraint
table: depos
column: railroad_system_id
Failing row contains (3, null, d387a91b-db77-4758-87ed-9951d5c2de8a, A depo, 23).
stacktrace:
(ecto_sql) lib/ecto/adapters/sql.ex:621: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto) lib/ecto/repo/schema.ex:649: Ecto.Repo.Schema.apply/4
(ecto) lib/ecto/repo/schema.ex:262: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(ecto) lib/ecto/association.ex:927: Ecto.Association.BelongsTo.on_repo_change/5
(ecto) lib/ecto/association.ex:413: Ecto.Association.on_repo_change/7
(elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/association.ex:392: Ecto.Association.on_repo_change/4
(ecto) lib/ecto/repo/schema.ex:811: Ecto.Repo.Schema.process_parents/4
(ecto) lib/ecto/repo/schema.ex:242: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(ecto) lib/ecto/association.ex:662: Ecto.Association.Has.on_repo_change/5
(ecto) lib/ecto/association.ex:432: anonymous fn/8 in Ecto.Association.on_repo_change/7
(elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/association.ex:428: Ecto.Association.on_repo_change/7
(elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/association.ex:392: Ecto.Association.on_repo_change/4
(ecto) lib/ecto/repo/schema.ex:837: Ecto.Repo.Schema.process_children/5
(ecto) lib/ecto/repo/schema.ex:914: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
(ecto_sql) lib/ecto/adapters/sql.ex:890: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection) lib/db_connection.ex:1415: DBConnection.run_transaction/4
(railroad_server) lib/railroad_server/database.ex:61: RailroadServer.Database.insert_railway_system/4
Versions:
Elixir - 1.9.0
Ecto - 3.17
Postgrex - 0.14.3
Prosgres - 11.4
When I try an insert with identical code as in your insert_railway_system(), I do not get a NULL column error.
My schemas are similar. The only significant difference in my code is in the changeset where I have the constraint:
|> assoc_constraint()
instead of:
|> foreign_key_constraint()
But I changed my code to try the foreign_key_constraint(), leaving the argument the same, which is equivalent to your :railroad_system, and the insert still worked. The insert also worked when I did the equivalent of foreign_key_constraint(:railroad_system_id). In fact, if I use foreign_key_constraint(:hello_world), the insert still works, so as far as I can tell, the second argument to foreign_key_constraint() is ignored, which is puzzling. I even did mix ecto.reset, which deletes the repo/database, recreates the repo/database, then executes the migrations, which creates the tables in the repo/database, and I got the same results.
My "create_depos" migration has the equivalent of the following:
add :railroad_system_id, references(:railroad_systems)
Please post:
The create_depos() function (although for me just using an attributes map instead of a changeset also worked)
The full stack trace of the error.
Your migrations.
The sql may be like:
SELECT * FROM TABLE WHERE field0=#{f0} AND field1=#{f1} AND field2=#{f2};
The restriction f0 or f1 or f2 may be nil or a predefined value to state that no restrictions for that field.
How to elegantly express this in Rails using ActiveRecord?
Use a hash of the attributes and values in your where query, using reject to remove the empty values:
attributes = { field0: 'field0_value', field1: '', field2: 'field2_value' }
MyModel.where( attributes.reject { |k,v| v.blank? } )
Will result in:
SELECT * FROM my_models WHERE field0='field0_value' AND field2='field2_value'
If you have rails 3 this will work .where(:field0 => f0, :field1 => f1, :field2 => f2) Rails 3: How to find records with field possibly equals to nil? If Rails 4, try .where(field0: f0, field1: f1, field2: f2)
Currently I have problem in defining a boolean variable :
I have a class with a boolean variable:
Pet:
isFeline ?= 'cat' | isFeline ?= 'dog' ;
However this result in Pet returning with 'cat'/'dog' as true. Is there anyway to define DSL: 'cat' as true and 'dog' as false ??
Maybe you can try this:
Pet:
{Pet} (isFeline?='cat' | 'dog');
Normally it should do what you want!
How do I translate:
SELECT COUNT(*) AS `count`, `a` FROM `b` GROUP BY `a` ORDER BY `a`
into grails or gorm query?
Since grails 1.2 you can create aliases and order by the created alias.
See https://cvs.codehaus.org/browse/GRAILS-3875 and https://cvs.codehaus.org/browse/GRAILS-3655 for more details.
Applied to your own code, the HQL query would be :
def c = b.createCriteria()
def results = c {
projections {
groupProperty("a")
count("a", 'myCount') //Implicit alias is created here !
}
order 'myCount'
}
working in grails 1.2.1
def c = C.createCriteria()
def pl = c.list {
projections {
countDistinct 'id', 'myCount'
groupProperty 'a'
}
order ('myCount', 'desc')
}
the answer is for example
[[10,a3],[2,a1],[1,a2]]
I would try
def c = b.createCriteria()
def results = c {
projections {
groupProperty("a")
rowCount()
}
order("a")
}
Note this is untested.