passive_columns 0.1.2 → 0.2.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: a9d9611e28f0d1a9a3bcb4c048158d3757294c0e13e2b3cf17f1912c62bf0886
4
- data.tar.gz: 7650f8e0c2da1e72d9ddeb29b3be36c900ce2e3b887a59affcb13b6da531a270
3
+ metadata.gz: 56f6f5fdf5c333ca2fe7ba55e3974b21b67662362a21848b1b31cf6216e080a1
4
+ data.tar.gz: adbcac3dfba991402e20dc7fc3d2a616498af1998ae4b0d5b4a1a9895254b2c4
5
5
  SHA512:
6
- metadata.gz: 103734dbe62aad0bf4a956c9fafa6ac69d0f92f4f57f673b3cff7329f6b854a02d69d561442b5a812d2c2416b426f2f5a8bd072f590b52a70b0a8b4023dc86bf
7
- data.tar.gz: d7c56d6635c3537c472cd5286a29735c9b6e0ccd06d515a68de64367e16a58ee24c32f540e2f76635c17091a990c08ca8a3f46c9b49b3bfb888385876eb69877
6
+ metadata.gz: 9ca11045bc3d47d290e6797c3b834eb4f1d8770ffd955c3be392b51c5d796bd2049c2ae7c849e85f189ce4e91524928cad8a5d55463fc36c082bc54c74c47aa8
7
+ data.tar.gz: df054d53f99a0e292292fe6935ba4dc3e2d707856372043468de0afc121065d9ca515ffb528d7292aba9a5b7f378114ac55b7d8b7e640268ceae897351c18cb1
data/README.md CHANGED
@@ -1,11 +1,73 @@
1
- # PASSIVE_COLUMNS
2
- Short description and motivation.
1
+ # Passive Columns
2
+ A gem that extends `Active Record` to retrieve columns from DB on demand.<br>
3
+ Works with `Rails` >= 7 and `Ruby` >= 2.7
3
4
 
4
5
  ## Usage
5
- How to use my plugin.
6
+
7
+ ```ruby
8
+ class Page < ApplicationRecord
9
+ include PassiveColumns
10
+ passive_columns :huge_article
11
+ end
12
+ ```
13
+
14
+ `ActiveRecord::Relation` now retrieves all the columns except the passive ones by default.
15
+ ```ruby
16
+ article = Page.where(status: :active).to_a
17
+ # => SELECT "pages"."id", "pages"."status", "pages"."title" FROM "pages" WHERE "pages"."status" = 'active'
18
+ ```
19
+
20
+ If you specify the columns via select it retrieves only the specified columns and nothing more.
21
+ ```ruby
22
+ page = Page.select(:id, :title).take # => #<Page id: 1, title: "Some title">
23
+ page.to_json # => {"id": 1, "title": "Some title"}
24
+
25
+ ```
26
+
27
+ But you still has an ability to retrieve the passive column on demand
28
+
29
+ ```ruby
30
+ page.huge_article
31
+ # => SELECT "pages"."huge_article" WHERE "pages"."id" = 1 LIMIT 1
32
+ 'Some huge article...'
33
+
34
+ page.to_json # => {"id": 1, "title": "Some title", "huge_article": "Some huge article..."}
35
+
36
+ # The next time you call the passive column it won't hit the database as it is already loaded.
37
+ page.huge_article # => 'Some huge article...'
38
+ ```
39
+
40
+ ---
41
+
42
+
43
+ Another way to get columns on demand is to use the `load_column` method.
44
+
45
+ This method loads a column value, if not already loaded, from the database
46
+ regardless of whether the column is added to `passive_columns` or not.
47
+
48
+ ```ruby
49
+ class User < ActiveRecord::Base
50
+ include PassiveColumns
51
+ end
52
+ ```
53
+ ```ruby
54
+ user = User.select('id').take!
55
+ user.name # missing attribute 'name' for User (ActiveModel::MissingAttributeError)
56
+
57
+ user.load_column(:name) # => SELECT "name" FROM "users" WHERE "id" = ? LIMIT ?
58
+ 'John'
59
+ user.load_column(:name) # no additional query. It's already loaded
60
+ 'John'
61
+
62
+ user.name
63
+ 'John'
64
+ ```
65
+
66
+ By the way, it uses the Rails' `.pick` method to get the value of the column under the hood
67
+
6
68
 
7
69
  ## Installation
8
- Add this line to your application's Gemfile:
70
+ Add this line to your Gemfile:
9
71
 
10
72
  ```ruby
11
73
  gem "passive_columns"
@@ -13,7 +75,7 @@ gem "passive_columns"
13
75
 
14
76
  And then execute:
15
77
  ```bash
16
- $ bundle
78
+ $ bundle install
17
79
  ```
18
80
 
19
81
  Or install it yourself as:
@@ -21,8 +83,53 @@ Or install it yourself as:
21
83
  $ gem install passive_columns
22
84
  ```
23
85
 
24
- ## Contributing
25
- Contribution directions go here.
86
+ # Motivation
87
+
88
+ There are situations when you have an `Active Record` model with columns
89
+ that you don't want to fetch from a DB every time you manipulate the model.
90
+
91
+ What options do you have?
92
+
93
+ ```ruby
94
+ # You can declare a scope to exclude columns dynamically from the select settings.
95
+ scope :skip_retrieving, ->(*v) { select(column_names.map(&:to_sym) - Array.wrap(v)) }
96
+ # or you can select only the columns you need
97
+ scope :only_main_columns, -> { select(%w[id name description uuid]) }
98
+
99
+ # When it's really important to skip unnecessary columns, you can use the default scope.
100
+ default_scope { :only_main_columns }
101
+ ```
102
+
103
+ At first glance, it seems like a good solution.
104
+ Until you realize that you cannot manipulate the model without the columns you skipped, as there are validation rules related to them.
105
+
106
+ ```ruby
107
+
108
+ class Project < ActiveRecord::Base
109
+ scope :only_main_columns, -> { select(%w[id name description uuid]) }
110
+
111
+ validates :id, :name, presence: true
112
+ validates :settings, presence: true
113
+ end
114
+
115
+
116
+ p = Project.only_required_columns.take
117
+ p.update!(name: 'New name') # missing attribute 'settings' for Project (ActiveModel::MissingAttributeError)
118
+
119
+ ```
120
+ One way to avoid this is to check for the presence of the attribute before validating it.
121
+
122
+ ```ruby
123
+ validates :huge_article, presence: true, if: -> { attributes.key?('huge_article') }
124
+ ```
125
+
126
+ Unfortunately, boilerplate code is needed for such a simple task.
127
+ You just wanted to exclude some columns and be able to manipulate a model without extra steps.
128
+
129
+ `passive_columns` tries to solve this problem by allowing you to exclude columns from the selection
130
+ and also allows you to retrieve them on demand when needed.
131
+
132
+
26
133
 
27
134
  ## License
28
135
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -5,19 +5,23 @@ module PassiveColumns
5
5
  # to automatically select all columns except passive columns if no columns are selected.
6
6
  module ActiveRecordRelationExtension
7
7
  def exec_main_query(**args)
8
- _set_columns_except_passive_if_nothing_selected
8
+ if klass.try(:_passive_columns).present? && select_values.blank?
9
+ self.select_values = klass.column_names - klass._passive_columns
10
+ end
9
11
  super
10
12
  end
11
13
 
12
14
  def to_sql
13
- _set_columns_except_passive_if_nothing_selected
14
- super
15
- end
15
+ return @to_sql unless @to_sql.nil?
16
16
 
17
- def _set_columns_except_passive_if_nothing_selected
18
- return nil if klass.try(:_passive_columns).blank? || select_values.any?
17
+ # @see ActiveRecord::QueryMethods::assert_mutability!
18
+ return super if @loaded || (defined?(@arel) && @arel)
19
19
 
20
- self.select_values = klass.column_names - klass._passive_columns
20
+ if klass.try(:_passive_columns).present? && select_values.blank?
21
+ self.select_values = klass.column_names - klass._passive_columns
22
+ end
23
+
24
+ super
21
25
  end
22
26
  end
23
27
  end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # @!attribute [r] lazy_columns
4
- # @return [Array<Symbol>]
5
- # @!attribute [r] model
6
- # @return [LazyColumns]
7
3
  module PassiveColumns
8
4
  # Loader is a class helper that loads a column value from the database if it is not loaded yet.
9
5
  class Loader
@@ -1,17 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # require 'passive_columns/active_record_relation_extension'
4
-
5
- # module PassiveColumns # :nodoc:
6
- # class Railtie < Rails::Railtie # :nodoc:
7
- # config.to_prepare do |_app|
8
- # ActiveSupport.on_load(:active_record) do
9
- # ActiveRecord::Relation.prepend(ActiveRecordRelationExtension)
10
- # end
11
- # end
12
- # end
13
- # end
14
-
15
3
  require 'passive_columns/active_record_relation_extension'
16
4
  require 'passive_columns/active_record_association_builder_extension'
17
5
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PassiveColumns
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.1'
5
5
  end
@@ -85,7 +85,7 @@ module PassiveColumns
85
85
  opts = filter_list.extract_options!
86
86
  if name == :validate && opts[:attributes]&.one?
87
87
  passive_column = opts[:attributes].map(&:to_s) & _passive_columns
88
- opts[:if] = ([-> { attributes.key?(passive_column) }] + Array(opts[:if])) if passive_column.present?
88
+ opts[:if] = ([-> { attributes.key?(passive_column.first) }] + Array(opts[:if])) if passive_column.present?
89
89
  end
90
90
  super(name, *filter_list, opts, &block)
91
91
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passive_columns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Golovin