Following is reproduce:
- mkdir test && cd ./test
- mkdir -p spec/graphql && touch spec/spec_helper.cr && touch spec/graphql/hello_spec.cr
- Add following content into spec/spec_helper.cr
macro query(query_path = "")
{% root = system("pwd").strip.id %}
macro strip_path
\{%
if {{query_path}} != ""
graphql_queries_path = "./spec/graphql_queries/{{query_path.id}}.graphql"
else
graphql_queries_path = __FILE__
.gsub(%r{{{root}}}, ".")
.gsub(\%r{graphql}, "graphql_queries")
.gsub(\%r{_spec\.cr}, ".graphql")
end
%}
\{% if file_exists? graphql_queries_path %}
\{{read_file(graphql_queries_path)}}
\{% else %}
raise "`\{{graphql_queries_path.id}}' not exists!"
\{% end %}
end
p strip_path.chomp
end
-
echo ‘require “…/spec_helper”; query’ > spec/graphql/hello_spec.cr
-
Run
crystal run spec/graphql/hello_spec.cr
, it raise a exception as expected.
Unhandled exception:
./spec/graphql_queries/hello.graphql’ not exists! (Exception)`
Basically, i want to do the same thing as the caller hack in this answer, but use macro, at compile time.
Let us use above files as a example, when query
is invoke, i want to know where it be invoke, in this cause, it is spec/graphql/hello_spec.cr
, then, i transform this path to spec/graphql_queries/a/b/hello.graphql
, then read file content from it.
Following is my questions:
Question 1
Why the %
in 1 don’t need escape, but, 2,3 must escaped?
otherwise, replace the \%r
into %r
for 2, will report following error?
╰─ $ crystal run spec/graphql/hello_spec.cr
In spec/spec_helper.cr:1:7
1 | macro query(query_path = "")
^
Error: unterminated macro
BTW, if the the Regexp's pattern of 2 was not matched, e.g. . `%r{hello}`, not raise above error.
Anyway, i consider this maybe a bug of macro?
Question 2
In fact, this macro not work as expected.
we create the missing graphql file first.
- mkdir -p ./spec/graphql_queries
- echo “query { hello() {}}” > ./spec/graphql_queries/hello.graphql
- Run hello_spec.cr, it works as expected.
╰─ $ crystal run spec/graphql/hello_spec.cr
“query { hello() {}}”
- Then, change the content of
spec/graphql/hello_spec.cr
into../spec_helper"; p query
(add a p before query macro), get following error.
╰─ $ crystal run spec/graphql/hello_spec.cr
Showing last frame. Use --error-trace for full trace.
There was a problem expanding macro 'query'
Code in spec/graphql/hello_spec.cr:1:29
1 | require "../spec_helper"; p query
^
Called macro defined in spec/spec_helper.cr:1:1
1 | macro query(query_path = "")
Which expanded to:
> 1 |
> 2 |
> 3 | macro strip_path
^---------
Error: can't declare macro dynamically
Question 3
Finally, I done this use a method, on the runtime (as ruby does)
def query(query_path="")
if query_path != ""
graphql_queries_path = "./spec/graphql_queries/#{query_path}.graphql"
else
graphql_queries_path = caller[2][/(.+?):\d+.*/, 1].gsub("graphql", "graphql_queries").gsub("_spec.cr", ".graphql")
end
if File.exists? graphql_queries_path
File.read(graphql_queries_path)
else
raise "`File #{graphql_queries_path}' not exists!"
end
end
But, i still consider we can do same thing use macro on the compile time, right?
Thank you.