Elixir alias on a submodule - module

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

Created Expected created to be (Time | Nil) but got (Float64 | String)

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.

Getting Ecto to pass foreign keys

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.

Rails-In ActiveRecord, how to express elegantly for omitting where conditions when given param is nil or a predefined const?

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)

Xtext DSL: boolean rule with 2 different string

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 to do Group By in grails to order by Count(*)

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.