https://github.com/pioz/plucker
Pluck database records in structs.
https://github.com/pioz/plucker
activerecord database query ruby
Last synced: 2 months ago
JSON representation
Pluck database records in structs.
- Host: GitHub
- URL: https://github.com/pioz/plucker
- Owner: pioz
- License: mit
- Created: 2023-11-17T11:46:00.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-11-20T13:59:51.000Z (over 1 year ago)
- Last Synced: 2025-02-02T03:49:31.643Z (3 months ago)
- Topics: activerecord, database, query, ruby
- Language: Ruby
- Homepage:
- Size: 22.5 KB
- Stars: 13
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README

[](https://codecov.io/gh/pioz/plucker)# Plucker
Plucker allows projecting records extracted from a query into an array of
specifically defined [Ruby structs](https://ruby-doc.org/current/Struct.html) for the occasion. It is an
enchanted [`pluck`](https://www.rubydoc.info/docs/rails/ActiveRecord%2FCalculations:pluck). It
takes a list of values you want to extract and throws them into a custom
array of Ruby struct.This can make your application more efficient because it avoids loading
ActiveRecord objects and utilizes structs, which are more efficient.## Installation
Install the gem and add to the application's Gemfile by executing:
$ bundle add plucker
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install plucker
## Usage
```ruby
posts = Post.joins(:author, :comments).group(:id).plucker(:title, 'authors.name', { comments_count: 'COUNT(comments.id)' })
post = posts.first
post.title # 'How to make pizza'
post.authors_name # 'Henry'
post.comments_count # 2
post.id # NoMethodError: undefined method `id' for #
```## Purpose
Let's assume we have these classes:
```ruby
class Author < ApplicationRecord
has_many :postvalidates :name, presence: true
endclass Post < ApplicationRecord
belongs_to :authorvalidates :title, body, presence: true
def slug
self.title.parameterize
end
end
```and we execute this query:
```ruby
posts = Post.joins(:author).select('id, authors.name AS author_name')
```The objects in the posts array are ActiveRecord objects of type `Post`. As I
read the code, it feels natural for me to be able to do:```ruby
post = posts.first
post.id
post.title
post.body
post.slug
```Now, out of these instructions, only `post.id` works, while all the others
will result in an error because the fields were not selected. This is very
strange to me, and in a complex codebase, it can lead to confusion and
frustration.Furthermore, I can see in the code `post.author_name` and wonder where that
method or column is defined. Obviously, I won't find the definition of that
method because it is dynamically generated by ActiveRecord. I don't like this
very much; it makes it unclear what data is present in the object.Therefore, I have decided to write Plucker to have well-defined objects with
clear fields right from the start. I aim for lightweight and efficient
objects without creating ActiveRecord fat objects with methods that I can't
even use.Keep in mind that you can always continue to perform queries in the standard
ActiveRecord way. With Plucker, you have a new, more efficient, and clearer
option.## Doc
The arguments of Plucker can be specified in in 3 different ways depending on
the requirements: as a `Symbol`, as a `String`, or as a `Hash`.When using a symbol, the column with the corresponding name to the symbol is
selected, and the struct field will have that name:```ruby
post = Post.plucker(:title).last
#post = Post.joins(:author).plucker(:title, :name).last
#
```When using the symbol `:*`, it is interpreted as `SELECT *` statement,
selecting all columns from the specified table:```ruby
post = Post.plucker(:*).last
#
```When using a string value, the name of the struct field will be generated
using the [`parameterize`](https://www.rubydoc.info/gems/activesupport/String#parameterize-instance_method)
function with an underscore as the separator:```ruby
post = Post.joins(:comments).plucker('posts.title', 'COUNT(comments.id)').last
#
```When using the string `table_name.*`, it is interpreted as `SELECT
table_name.*` statement, selecting all columns from the specified table:```ruby
post = Post.joins(:author).plucker(:*, 'authors.*').last
#
```When using a Hash, it operates similarly to the String case, except that the
name of the struct field will be the same as the key of the Hash:```ruby
post = Post.joins(:comments).plucker(:title, comments_count: 'COUNT(comments.id)').last
#
```Plucker also takes an optional block, which is passed to the struct
definition:```ruby
posts = Post.plucker(:title) do
def slug
self.title.parameterize
enddef as_json
super.tap do |json|
json['slug'] = self.slug
end
end
end.last
#post.title
# 'How to make pizza'
post.slug
# 'how-to-make-pizza'
post.as_json
# {"title"=>"How to make pizza", "slug"=>"how-to-make-pizza"}
```## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/pioz/plucker.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).