better_pluck 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64a910349a333fa7833d93fc87015f91b11344588c3b6fc3c2635b89d9a85960
4
- data.tar.gz: 3b88f50b8e75d588849954b9d27dd46f006cdb94ee70890e4f5ed87a69f14a5d
3
+ metadata.gz: 30cec08ad4a30c45757e0c5cc9043c5748f092b7657faae3610f17888404bb80
4
+ data.tar.gz: 9309acc55f803205de2fb2e0a2055df8d6b56e1d45f13ee2f0884265b67b17bd
5
5
  SHA512:
6
- metadata.gz: 49329adec0a9d95bac2c321fb4dbe30b68c8c194c7bc57d4102e8ce177216ca2c9482dda07698ab90f04c5172483e8588fc81fdb13bc8d08a5b680500e67a2bb
7
- data.tar.gz: 725142f9eaab1335c370d8da493463df52b473e155ef25c1428314b034e3b3126e39338b274d23e558b529f1ec745413179a3e9e19daf5c7de0eeb67ab59b318
6
+ metadata.gz: 524eaeb0e4f7d7c4677cf5a8fdb42a8fc5bfc6768a963c7212d6cc74aa1a61b86f144f45545d10cff6f74c61f3b12f17bd0acae7e939001b81ae7574b0c38537
7
+ data.tar.gz: d2c359630bfcbe6f1b71cc3115f7df5f72b2a29e72cc317bfa76753431aef46b6f001214dbca87afd82e8bf2341d88c9ef7c9eeffdab7b1fdec70b96aee16266
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  Main purpose is to reduce loading into memory expensive ActiveRecord objects, where it is not needed (for read-only operations).
5
5
 
6
- `better_pluck` adds `pluck_with_methods`, `pluck_with_display_name` to ActiveRecord. These methods allow you to pluck not only database columns but also virtual methods and association data, returning lightweight `Struct` objects instead of heavy ActiveRecord instances.
6
+ `better_pluck` adds `struct_pluck`, `struct_pluck_with_display_name` to ActiveRecord. These methods allow you to pluck not only database columns but also virtual methods and association data, returning lightweight `Struct` objects instead of heavy ActiveRecord instances.
7
7
 
8
8
  This approach can reduce memory usage by up to 3-20x compared to loading full ActiveRecord objects.
9
9
 
@@ -17,10 +17,6 @@ Add this line to your application's Gemfile:
17
17
  gem 'better_pluck'
18
18
  ```
19
19
 
20
- And then execute:
21
-
22
- $ bundle install
23
-
24
20
  ## Usage
25
21
 
26
22
  ### Basic usage
@@ -29,7 +25,7 @@ You can pluck database columns and instance methods:
29
25
 
30
26
  ```ruby
31
27
  # Assuming Article has a method :name_on_site
32
- articles = Article.pluck_with_methods(:id, :name, :email, :name_on_site)
28
+ articles = Article.struct_pluck(:id, :name, :email, :name_on_site)
33
29
 
34
30
  articles.first.id # => 1
35
31
  articles.first.name_on_site # => "My Article" (virtual method)
@@ -40,7 +36,7 @@ articles.first.name_on_site # => "My Article" (virtual method)
40
36
  You can also pluck data from nested associations:
41
37
 
42
38
  ```ruby
43
- articles = Article.pluck_with_methods(
39
+ articles = Article.struct_pluck(
44
40
  :id,
45
41
  :name,
46
42
  author: [:name, :email, :display_name]
@@ -56,24 +52,15 @@ A convenience method specifically for dropdown lists and collections:
56
52
 
57
53
  ```ruby
58
54
  # Automatically includes :display_name for the model and associations
59
- articles = Article.pluck_with_display_name(:id, :name, author: [:name, :email])
55
+ articles = Article.struct_pluck_with_display_name(:id, :name, author: [:name, :email])
60
56
 
61
57
  articles.first.display_name # => "Article Name"
62
58
  articles.first.author.display_name # => "Author Name <author@example.com>"
63
59
  ```
64
60
 
65
- ## How it works
66
-
67
- 1. **Parsing**: It parses the requested fields to distinguish between database columns, methods, and associations.
68
- 2. **Joining**: It automatically applies `left_joins` for requested associations.
69
- 3. **Plucking**: It uses the standard ActiveRecord `pluck` to fetch only the required database columns.
70
- 4. **Struct Generation**: It creates a `Struct` class (cached for performance) and injects the source code of requested instance methods into it.
71
- 5. **Instantiation**: It maps the raw data into these `Struct` objects.
72
-
73
-
74
61
  ### select_with_joins method
75
62
 
76
- You can use the `select_with_joins` method which will create field aliases for associations instead of fully fetching activerecord objects for them. The main model can be selected with specific columns or fully fetched.
63
+ You can use the `select_with_joins` method which will create field aliases for associations instead of fully fetching activerecord objects for them. The main model can be selected with specific columns or fully fetched. Main model is kept as ActiveRecord object.
77
64
 
78
65
  ```ruby
79
66
  # Example with positional arguments for base model
@@ -1,12 +1,12 @@
1
1
  # this module is supposed to be included in ActiveRecord descendants
2
2
  # to reduce ram usage by converting arrays of AR objects to Struct objects
3
- # in particular for dropdown lists (in f.input collection: Article.pluck_with_display_name(:id,:name,:email), as: :select)
3
+ # in particular for dropdown lists (in f.input collection: Article.struct_pluck(:id,:name,:email), as: :select)
4
4
  # also takes associations display_name from their ActiveRecord class
5
- # usage: zones = Zone.pluck_with_display_name(:id,:name, partner: [:имя,:email], location: [:name])
5
+ # usage: zones = Zone.struct_pluck_with_display_name(:id,:name, partner: [:имя,:email], location: [:name])
6
6
  # zones.first.display_name, zones.first.partner.display_name
7
7
 
8
- #pluck_with_methods method also available where you can list any fields and methods
9
- #example: articles = Article.pluck_with_methods(:id, :name, :email, :name_on_site, author: [:name, :email,:display_name])
8
+ #struct_pluck method also available where you can list any fields and methods
9
+ #example: articles = Article.struct_pluck(:id, :name, :email, :name_on_site, author: [:name, :email,:display_name])
10
10
  #
11
11
  #in returned result you can use:
12
12
  #article.first.name_on_site, article.first.author.email, articles.first.author.display_name etc.
@@ -15,7 +15,7 @@
15
15
 
16
16
  #NK 2026-10-13, done with Gemini AI
17
17
 
18
- module BetterPluck::PluckWithMethods
18
+ module BetterPluck::StructPluck
19
19
  extend ActiveSupport::Concern
20
20
 
21
21
  # Use Concurrent::Map for thread-safe caching across multiple Puma threads
@@ -23,33 +23,33 @@ module BetterPluck::PluckWithMethods
23
23
 
24
24
  class_methods do
25
25
 
26
-
27
-
28
- # usage: zones = Zone.pluck_with_methods(:id, :name, :display_name, partner: [:имя, :email, :display_name], location: [:name, :display_name])
29
- def pluck_with_methods(*fields_and_methods)
26
+ # usage: zones = Zone.struct_pluck(:id, :name, :display_name, partner: [:имя, :email, :display_name], location: [:name, :display_name])
27
+ def struct_pluck(*fields_and_methods)
30
28
  require "method_source" unless defined?(MethodSource)
31
29
 
32
- metadata = _pluck_methods_parse_fields(self, fields_and_methods)
30
+ metadata = _struct_pluck_parse_fields(self, fields_and_methods)
33
31
  pluck_columns = []
34
- _pluck_methods_build_columns(self, metadata, pluck_columns, self.table_name)
32
+ _struct_pluck_build_columns(self, metadata, pluck_columns, self.table_name)
35
33
 
36
- joins_arg = _pluck_methods_build_joins(metadata)
34
+ joins_arg = _struct_pluck_build_joins(metadata)
37
35
 
38
36
  query = joins_arg.present? ? left_joins(joins_arg) : all
39
37
  raw_rows = query.pluck(*pluck_columns)
40
38
 
41
39
  # Root struct class
42
- root_struct_klass = _pluck_methods_get_struct(metadata)
40
+ root_struct_klass = _struct_pluck_get_struct(metadata)
43
41
 
44
42
  raw_rows.map do |row|
45
43
  row_data = row.is_a?(Array) ? row.dup : [row]
46
- _pluck_methods_instantiate(root_struct_klass, metadata, row_data)
44
+ _struct_pluck_instantiate(root_struct_klass, metadata, row_data)
47
45
  end
48
46
  end
49
47
 
50
- # usage: zones = Zone.pluck_with_display_name(:id,:name, partner: [:имя,:email], location: [:name])
48
+ alias_method :pluck_with_methods, :struct_pluck
49
+
50
+ # usage: zones = Zone.struct_pluck_with_display_name(:id,:name, partner: [:имя,:email], location: [:name])
51
51
  # zones.first.display_name, zones.first.partner.display_name
52
- def pluck_with_display_name(*fields_and_methods)
52
+ def struct_pluck_with_display_name(*fields_and_methods)
53
53
  fields_and_methods << :display_name unless fields_and_methods.include?(:display_name)
54
54
 
55
55
  processed_params = fields_and_methods.map do |arg|
@@ -64,12 +64,14 @@ module BetterPluck::PluckWithMethods
64
64
  end
65
65
  end
66
66
 
67
- pluck_with_methods(*processed_params)
67
+ struct_pluck(*processed_params)
68
68
  end
69
69
 
70
+ alias_method :pluck_with_display_name, :struct_pluck_with_display_name
71
+
70
72
  private
71
73
 
72
- def _pluck_methods_parse_fields(klass, fields)
74
+ def _struct_pluck_parse_fields(klass, fields)
73
75
  db_columns = []
74
76
  methods = {}
75
77
  associations = {}
@@ -88,7 +90,7 @@ module BetterPluck::PluckWithMethods
88
90
  assoc_fields = item[assoc_name]
89
91
  assoc_reflection = klass.reflect_on_association(assoc_name)
90
92
  if assoc_reflection
91
- associations[assoc_name.to_sym] = _pluck_methods_parse_fields(assoc_reflection.klass, Array(assoc_fields))
93
+ associations[assoc_name.to_sym] = _struct_pluck_parse_fields(assoc_reflection.klass, Array(assoc_fields))
92
94
  all_fields << assoc_name.to_sym
93
95
  end
94
96
  end
@@ -119,7 +121,7 @@ module BetterPluck::PluckWithMethods
119
121
  }
120
122
  end
121
123
 
122
- def _pluck_methods_build_columns(klass, metadata, list, table_alias)
124
+ def _struct_pluck_build_columns(klass, metadata, list, table_alias)
123
125
  metadata[:db_columns].each do |col|
124
126
  list << Arel.sql("#{klass.connection.quote_table_name(table_alias)}.#{col}")
125
127
  end
@@ -127,15 +129,15 @@ module BetterPluck::PluckWithMethods
127
129
  metadata[:associations].each do |assoc_name, sub_metadata|
128
130
  assoc_reflection = klass.reflect_on_association(assoc_name)
129
131
  # AR's left_joins usually uses the table name for the join alias unless there's a collision
130
- _pluck_methods_build_columns(assoc_reflection.klass, sub_metadata, list, assoc_reflection.table_name)
132
+ _struct_pluck_build_columns(assoc_reflection.klass, sub_metadata, list, assoc_reflection.table_name)
131
133
  end
132
134
  end
133
135
 
134
- def _pluck_methods_build_joins(metadata)
136
+ def _struct_pluck_build_joins(metadata)
135
137
  return nil if metadata[:associations].empty?
136
138
 
137
139
  joins = metadata[:associations].map do |assoc_name, sub_metadata|
138
- sub_joins = _pluck_methods_build_joins(sub_metadata)
140
+ sub_joins = _struct_pluck_build_joins(sub_metadata)
139
141
  if sub_joins
140
142
  { assoc_name => sub_joins }
141
143
  else
@@ -146,7 +148,7 @@ module BetterPluck::PluckWithMethods
146
148
  joins.size == 1 ? joins.first : joins
147
149
  end
148
150
 
149
- def _pluck_methods_get_struct(metadata)
151
+ def _struct_pluck_get_struct(metadata)
150
152
  metadata[:struct_klass] ||= begin
151
153
  # 1st list: database fields and associations
152
154
  # 2nd list: virtual methods (copied from ActiveRecord)
@@ -163,19 +165,19 @@ module BetterPluck::PluckWithMethods
163
165
  end
164
166
  end
165
167
 
166
- def _pluck_methods_instantiate(struct_klass, metadata, row_data)
168
+ def _struct_pluck_instantiate(struct_klass, metadata, row_data)
167
169
  args = metadata[:all_fields].map do |field|
168
170
  if metadata[:db_columns].include?(field)
169
171
  row_data.shift
170
172
  elsif (sub_metadata = metadata[:associations][field])
171
- sub_struct_klass = _pluck_methods_get_struct(sub_metadata)
173
+ sub_struct_klass = _struct_pluck_get_struct(sub_metadata)
172
174
  sub_col_count = sub_metadata[:total_db_columns]
173
175
 
174
176
  if row_data[0...sub_col_count].all?(&:nil?)
175
177
  row_data.shift(sub_col_count)
176
178
  nil
177
179
  else
178
- _pluck_methods_instantiate(sub_struct_klass, sub_metadata, row_data)
180
+ _struct_pluck_instantiate(sub_struct_klass, sub_metadata, row_data)
179
181
  end
180
182
  else
181
183
  nil # Method field
@@ -1,3 +1,3 @@
1
1
  module BetterPluck
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
data/lib/better_pluck.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  require "better_pluck/version"
2
- require "better_pluck/pluck_with_methods"
2
+ require "better_pluck/struct_pluck"
3
3
  require "better_pluck/select_with_joins"
4
4
 
5
5
  module BetterPluck
6
6
  end
7
7
 
8
8
  ActiveSupport.on_load(:active_record) do
9
- include BetterPluck::PluckWithMethods
9
+ include BetterPluck::StructPluck
10
10
  include BetterPluck::SelectWithJoins
11
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_pluck
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - NazarK
@@ -117,8 +117,8 @@ files:
117
117
  - README.md
118
118
  - better_pluck.gemspec
119
119
  - lib/better_pluck.rb
120
- - lib/better_pluck/pluck_with_methods.rb
121
120
  - lib/better_pluck/select_with_joins.rb
121
+ - lib/better_pluck/struct_pluck.rb
122
122
  - lib/better_pluck/version.rb
123
123
  homepage: https://github.com/NazarK/better_pluck
124
124
  licenses: