The Crystal Programming Language Forum

Podcast about Lucky

I interviewed Paul Smith, who is the creator of Lucky. We spoke about Crystal and his path with working on the Lucky framework. I had a great time and think it will be a good episode for people interested in one of the largest web frameworks in Crystal. If you like it, please subscribe and review the podcast so we can get visibility in the podcast players.

12 Likes

@paulcsmith Do you have an example of what you meant by the index/show methods needing .not_nil!?

@blacksmoke16 Sure!

Just to clarify the context here is that you can’t have type-safe access to params when you have multiple methods in a single class. If you do something like params[:param_name] then you don’t need not_nil! but that is because there is never a guarantee that the param will exist.

So here is kind of what I mean by needing not_nil!

class MyController
  def index
     puts id # This will raise at Runtime because `id` is not set for this route
  end

  def show
    puts id # This will work
  end

  def id
    params[:id]
  end
end

So this doesn’t use not_nil! but illustrates that the param can be nil and can lead to Runtime errors.

You could use []? and not_nil! but you still have to manually ensure you are using it correctly. There is no compile-time guarantee

class MyController
  def index
     puts id.not_nil! # This will raise at Runtime because `id` is not set for this route
  end

  def show
    puts id.not_nil! # This will work
  end

  def id
    params[:id]?
  end
end

Whereas in Lucky each action has a route defined and thereforce can guarantee that a param exists at compile-time:

class Users::Show < BrowserAction
  # Lucky generates an 'id' method that returns the id
  route "/users/:id" do
    puts id # Works
  end
end

This also helps with path generation because the param is required at compile-time: Users::Show.path(id: 123). There are other ways to do that, but the class-based approach made it pretty simple.

Does that make sense?

EDIT: you actually can as @Blacksmoke16 showed below! I’d like to correct my previous statement to can’t do type safe to access params using instance methods/instance vars for the param (I think :slight_smile:).

Yes, thanks for the explanation.

However, this isn’t entirely true. To clarify that section in the podcast, Athena makes the route’s parameters, arguments in the method. It doesn’t fetch them another method like those examples or Rails does.

class UserController < ART::Controller
  @[ART::Get("/user")]
  def users : Array(User)
    [User.new(1, "User 1"), User.new(2, "User 2")]
  end

  @[ART::Get("/user/:id")]
  def user(id : Int32) : User
    User.new id, "User 1"
  end
end

Because of this, you get both compile time safety on arguments used within the method, as well as the return value of said method. Nilable arguments are considered optional and will be nil if not supplied (such as for query params for example). Since the actions are just methods, default values can also be used to keep an argument not nilable even if the parameter itself is not provided, say like include_all query param defaulted to false.

2 Likes

That’s pretty cool! I stand corrected. I prefer having them as instance vars but that is just preference.

Glad there are multiple ways to do type safety in crystal! I appreciate the various approaches

1 Like

@wontruefree you are doing a great job with these podcasts. They are really informative.

After listening to this most recent episode I’ve decided to try out lucky for a new project that is going to be fairly large, instead of using a React JAM Stack front end with Crystal microservices.

4 Likes

@paulcsmith in my above mentioned project, I may need to add in a rest API for most of the site functionality at a later date. Are there any lucky specific paradigms for adding an API alongside the site? Will I be able to easily use the same logic for both the site and the API?

1 Like

What I do in my site is I just have a folder src/actions/api/.... Inside my api folder are just namespaced actions

class Api::V1::Users::Index < ApiAction

end

class Users::Index < BrowserAction

end

Also note that by default, Lucky comes with both the ApiAction and BrowserAction to handle the two different use cases. Feel free to drop by the gitter chat if you have any questions!

1 Like

So happy to have you try Lucky! We have a number of people using REST APIs and can help you out if you get stuck. For keeping things DRY we have “Operations” which let you put the logic in an “Operation” class that can be called from a BrowserAction or ApiAction.

I hope you like using Lucky! It still has a ways to go for 1.0 but we’re getting close!

3 Likes

I am so glad you are enjoying them! They are fun to produce and hope they are a benefit to the community.

3 Likes