Hammerstone
You're a developer that wants
to focus on your job, but...
Users want
Visitors that have time
Marketing wants
People that have purchased product
Customer support wants
Users with an NPS score of
Executives want
Teams that have spent
every request wastes your time and pulls you away from the work you should be doing.
There's no repeatable process to fulfill these requests.
Marketing comes to you with their request. You write the custom SQL to find them the people that they're looking for. You run the query, export it to CSV, and give it back to them.
You get back to work, trying to figure out where you left off. You're starting to get into a flow state and then ping...
"Sorry to bother you, but instead of NPS less than 6, can you run NPS less than 5?"
It's a similar query, but it still requires you to break focus, change the SQL, and run a new report.
It's annoying.
But what can you do? These reports are business critical and the non-technical teams can’t build them without your help.
What if you could empower people to answer these questions themselves?
What if they could run NPS reports every day without ever bothering you?
No more context switching.
No more interruptions.
You can spend your time doing your job, not someone else's.
Give your team the power to build
advanced filters in your application with Refine.
Refine is a powerful drop-in visual query builder
for your Laravel and Rails applications.
App screenshot
Refine is a set of backend libraries and frontend components that work together seamlessly to allow your users to build queries. We provide Laravel and Rails backends, and Vue, React, and Hotwire frontends to match whatever stack you may be using.
You add Refine directly to your Rails or Laravel application using bundle install or composer require.
As the developer, you get to decide what fields are available to the end user. You set it up once by defining which models can be queried and we do the rest.
Refine provides a beautiful, customizable UI, it validates the user's input, and builds queries based on that input.

Before Refine I was spending at least an hour a day searching for teams with certain subscription states and pupil numbers. Now it takes me seconds.

Already convinced? Get started today.

How does it work?

After you've added Refine using bundle or composer you create a new Filter class that extends the class that ships with Refine.

Define an initial query. Refine builds up the final query based on the initial query and the user's input.

class UserFilter < Hammerstone::Refine::Filter
def initial_query
User.all
end
end
class UserFilter extends Filter
{
public function initialQuery()
{
return User::query();
}
}

Your users can filter data based on a set of conditions that you configure. Each condition corresponds to an attribute on your model.

You tell Refine the name of the column and its type, and we'll handle the rest.

class UserFilter < Hammerstone::Refine::Filter
def initial_query
User.all
end
 
def conditions
[
TextCondition.new("email"),
NumericCondition.new("nps_score")
]
end
end
class UserFilter extends Filter
{
public function initialQuery()
{
return User::query();
}
 
public function conditions()
{
return [
TextCondition::make('email'),
NumericCondition::make('nps_score')
];
}
}
This is the UI generated from the code above!

Refine gives you a set of prebuilt conditions that covers most use cases:

  • Text — starts with, contains, ends with, equals; we do it all.
  • Date, datetime, and timestamps — don't worry, we handle the timezones.
  • Boolean — true/false columns, with optional support for nulls.
  • Numeric — greater, less, equal, between; everything you'd expect.
  • Option — pick one or more from a defined set.

All of these can be combined with and/or groupings. And if those don't meet your needs, you can add your own custom conditions!

What about relationships?

Refine can handle that! If you need to access an attribute on a related model, simply add the attribute using dot notation.
If a User has an Address with a state_id that you want to include in your filter, you can add address.state_id as a condition.
class UsersFilter < Hammerstone::Refine::Filter
def initial_query
User.all
end
 
def conditions
[
TextCondition.new("email"),
NumericCondition.new("nps_score")
OptionCondition.new("address.state_id")
.with_display("State")
.with_options(State.all.pluck(:name, :id))
]
end
end
class UserFilter extends Filter
{
public function initialQuery()
{
return User::query();
}
 
public function conditions()
{
return [
TextCondition::make('email'),
NumericCondition::make('nps_score'),
OptionCondition::make('address.state_id')
->display('State')
->options(function() {
return State::query()->pluck('name', 'id');
})
];
}
}
From the user's perspective it's a simple query, even though you're going through a related model.

Ready to build your first filter? Get started today.