3

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

gurrurin
  • 41
  • 2
  • Do you think you can add the commands to reproduce it. I tried "amber new test" followed by "amber g scaffold item name:string path:string type:string size:float created:date", but could not reproduce. Also adding the output of "amber --version" would be helpful. – Philipp Claßen May 06 '20 at 17:19
  • This is the version i was using amber --version Amber CLI (amberframework.org) - v0.34.0 after running the new and g commands i also executed this amber db create migrate 09:49:46 Database | (INFO) Created database test_development 09:49:46 Database | (INFO) Migrating db, current version: 0, target: 20200507094722954 09:49:46 Database | (INFO) OK 20200507094722954_create_item.sql I tried several different values for created by this kep failing 09:50:29 Params | created: 2020-01-01 00:01:00 UTC 09:51:18 Params | created: 01/01/2020 10:25 – gurrurin May 07 '20 at 09:58

2 Answers2

1

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!
gurrurin
  • 41
  • 2
1

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.

Damian Hamill
  • 83
  • 1
  • 6
  • Thanks Damian, but where exactly am i supposed to add this line ? – gurrurin May 08 '20 at 11:26
  • If i add this here i still get the same error def create item = Item.new item_params.validate! item.created = Time.parse(params[:created],"%Y-%m-%d %H:%M:%S %z", Time::Location::UTC) if item.save – gurrurin May 08 '20 at 11:27
  • You don't need to add that line anywhere, I was just pointing out that if you wanted to set a column that represented a Time then you would set it to a Time instance. **created_at** is already a column in the table so you don't need an extra created column. But in answer to your question a line like that would be in the controller or in a method in the model that was doing some logic. – Damian Hamill May 09 '20 at 12:15