I have a CSV file that looks like this:
CountryCode,CountryName
AD,Andorra
AE,United Arab Emirates
AF,Afghanistan
AG,Antigua and Barbuda
// -- snip -- //
and a class that looks like this:
module OpenData
class Country
def initialize(@code : String, @name : String)
end
end
end
and I want to have a class variable within the module automatically loaded at compile time like this:
module OpenData
@@countries : Array(Country) = {{ run "./sources/country_codes.cr" }}
end
I tried to use the "run" macro above with the following code:
require "csv"
require "./country"
content = File.read "#{__DIR__}/country-codes.csv"
result = [] of OpenData::Country
CSV.new(content, headers: true).each do |row|
result.push OpenData::Country.new(row["CountryCode"], row["CountryName"])
end
result
but this results in
@@countries : Array(Country) = {{ run "./sources/country_codes.cr" }}
^
Error: class variable '@@countries' of OpenData must be Array(OpenData::Country), not Nil
All my other attempts somehow failed due to various reasons, like not being able to call .new within a macro or stuff like that. This is something I regularly do in Elixir and other languages that support macros and is something I would suspect Crystal can also achieve... I'd also take any other way that accomplishes the task at compile time!
Basically there are several more files I want to process this way, and they`re longer/more complex... thanks in advance!
EDIT:
Found the issue. It seems that I have to return a string that includes actual crystal code from the "run" macro. So, the code in the "run" file becomes:
require "csv"
content = File.read "#{__DIR__}/country-codes.csv"
lines = [] of String
CSV.new(content, headers: true).each do |row|
lines << "Country.new(\"#{row["CountryCode"]}\", \"#{row["CountryName"]}\")"
end
puts "[#{lines.join(", ")}]"
and everything works!