boosted-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,563 @@
1
+ # Boosted
2
+
3
+ Develop rails applications at the speed of thought. Boosted is a collection of tools and utilities that make it easier to develop rails applications.
4
+
5
+
6
+ ## Installation
7
+
8
+ Install the gem and add to the Rails application's Gemfile by executing:
9
+
10
+ $ bundle add boosted-rails
11
+
12
+ Then run the generator to create the configuration file:
13
+
14
+ $ rails generate boosted:install
15
+
16
+ The generator will create a file at `config/initializers/boosted.rb` with the default configuration.
17
+
18
+ ## Usage
19
+
20
+ Boosted provides utilities for making it easier to develop rails applications. The utilities are organized into modules and can be used by including the module in the class that needs the utility.
21
+ The utilities are organized in concepts used by most rails applications:
22
+ - [Controllers](#controllers)
23
+ - [Services](#services)
24
+ - [Jobs](#jobs)
25
+
26
+ ### Controllers
27
+
28
+ The `Boosted::Controllers` module defines five concerns that can be included in a controller to provide additional functionality:
29
+ - [Boosted::Controllers::Filterable](#boostedcontrollersfilterable)
30
+ - [Boosted::Controllers::Searchable](#boostedcontrollersearchable)
31
+ - [Boosted::Controllers::Sortable](#boostedcontrollerssortable)
32
+ - [Boosted::Controllers::Paginatable](#boostedcontrollerspaginatable)
33
+ - [Boosted::Controllers::Tabulatable](#boostedcontrollerstabulatable)
34
+
35
+ #### Boosted::Controllers::Filterable
36
+
37
+ The `Filterable` concern provides a method to filter the records in a controller's action.
38
+ The method `filterable_by` is used to define the filterable fields and the filter method to use.
39
+
40
+ ```ruby
41
+ class UsersController < ApplicationController
42
+ include Boosted::Controllers::Filterable
43
+
44
+ filterable_by :name, :email, :created_at
45
+
46
+ def index
47
+ users = filter(User.all)
48
+ render json: users
49
+ end
50
+ end
51
+ ```
52
+ The `filter` method will use the query parameters to filter the records. For example, to filter the users by name, email, and created_at, the following query parameters can be used:
53
+
54
+ ```
55
+ GET /users?name=John
56
+ GET /users?email=john@example.com
57
+ GET /users?created_at=2021-01-01
58
+ ```
59
+
60
+ ##### Referencing tables in the filterable fields
61
+
62
+ The `filterable_by` method can also be used to reference fields in associated tables. For example, to filter the users by the name of the company they work for, the following can be done:
63
+
64
+ ```ruby
65
+ class UsersController < ApplicationController
66
+ include Boosted::Controllers::Filterable
67
+
68
+ filterable_by :name, :email, :created_at, 'companies.name'
69
+
70
+ def index
71
+ users = filter(User.joins(:company))
72
+ render json: users
73
+ end
74
+ end
75
+ ```
76
+
77
+ Request examples:
78
+ ```
79
+ GET /users?name=John
80
+ GET /users?companies.name=Acme
81
+ ```
82
+
83
+ ##### Renaming the filter query parameters
84
+
85
+ If you don't want to use the field name as the query parameter (as to not expose the database schema, or when joining the same table multiple times),
86
+ you can specify the query parameter to use for each field:
87
+
88
+ ```ruby
89
+ class UsersController < ApplicationController
90
+ include Boosted::Controllers::Filterable
91
+
92
+ filterable_by 'companies.name' => :company_name, 'users.name' => :user_name
93
+
94
+ def index
95
+ users = filter(User.join(:company))
96
+ render json: users
97
+ end
98
+ end
99
+ ```
100
+
101
+ Request examples:
102
+ ```
103
+ GET /users?user_name=John
104
+ GET /users?company_name=Acme
105
+ ```
106
+
107
+ ##### Using filters other than `eq`
108
+
109
+ By default, the `filter` method will use the `eq` filter method to filter the records. If you want to use a different filter method, you can specify the filter "relation" in the query parameter:
110
+
111
+ ```ruby
112
+ class UsersController < ApplicationController
113
+ include Boosted::Controllers::Filterable
114
+
115
+ filterable_by :name, :age
116
+
117
+ def index
118
+ users = filter(User.all)
119
+ render json: users
120
+ end
121
+ end
122
+ ```
123
+
124
+ Request examples:
125
+ ```
126
+ GET /users?name=John # returns users with name John
127
+ GET /users?name[]=John&name[]=Jane # returns users where the name is in ('John', 'Jane')
128
+ GET /users?age.rel=is_null # returns users where the age is null
129
+ GET /users?age.rel=is_not_null # returns users where the age is not null
130
+ GET /users?age.rel=between&age[]=18&age[]=30 # returns users with age between 18 and 30
131
+ GET /users?age.rel=%3E%0A&age=18 # returns users with age greater than 18, %3E%0A is url encoded for ">"
132
+ ```
133
+
134
+ The full list of filter relations is:
135
+ - `=` (default) - equals
136
+ - `!=` - not equals
137
+ - `>` - greater than
138
+ - `>=` - greater than or equals
139
+ - `<` - less than
140
+ - `<=` - less than or equals
141
+ - `between` - between (requires two values)
142
+ - `in` - in (default when multiple values are provided)
143
+ - `not_in` - not in (requires multiple values)
144
+ - `starts_with` - starts with
145
+ - `ends_with` - ends with
146
+ - `contains` - contains
147
+ - `is_null` - is null (does not require a value)
148
+ - `is_not_null` - is not null (does not require a value)
149
+
150
+ #### Boosted::Controllers::Searchable
151
+
152
+ The `Searchable` concern provides a method to search the records in a controller's action.
153
+
154
+ By default it calls the scope `search` on the model, and uses the query parameter `q` to search the records.
155
+
156
+ ```ruby
157
+ # app/models/user.rb
158
+ class User < ApplicationRecord
159
+ # You can define your own search scope and use standard sql
160
+ # scope :search, ->(query) { where('name LIKE ?', "%#{query}%") }
161
+
162
+ # Or use pg_search
163
+ include PgSearch::Model
164
+ pg_search_scope :search, against: [:name, :email]
165
+ end
166
+
167
+ # app/controllers/users_controller.rb
168
+ class UsersController < ApplicationController
169
+ include Boosted::Controllers::Searchable
170
+
171
+ def index
172
+ users = search(User.all)
173
+ render json: users
174
+ end
175
+ end
176
+ ```
177
+
178
+ Request examples:
179
+ ```
180
+ GET /users?q=John
181
+ # calls #search(User.all, search_term: 'John', model_search_scope: :search) in the controller
182
+ ```
183
+
184
+ ##### Customizing the search query parameter
185
+
186
+ You can customize the default query parameter by:
187
+ 1. Passing fetching the fetch term from the params hash, and passing it directly to the search method:
188
+
189
+ ```ruby
190
+ class UsersController < ApplicationController
191
+ include Boosted::Controllers::Searchable
192
+
193
+ def index
194
+ # This will use the query parameter `term` instead of `q`
195
+ users = search(User.all, search_term: params[:term])
196
+ render json: users
197
+ end
198
+ end
199
+ ```
200
+
201
+ 2. Overriding the `search_param` method in the controller
202
+
203
+ ```ruby
204
+ class UsersController < ApplicationController
205
+ include Boosted::Controllers::Searchable
206
+
207
+ def index
208
+ # This will use the query parameter `term` instead of `q`
209
+ users = search(User.all)
210
+ render json: users
211
+ end
212
+
213
+ private
214
+
215
+ def search_param
216
+ :term
217
+ end
218
+ end
219
+ ```
220
+
221
+ 3. Calling #searchable_by in the controller and overriding the default query parameter
222
+
223
+ ```ruby
224
+ # app/models/user.rb
225
+ class User < ApplicationRecord
226
+ include PgSearch::Model
227
+ pg_search_scope :search_by_word, against: :name, using: { tsearch: { any_word: true } }
228
+ end
229
+
230
+
231
+ # app/controllers/users_controller.rb
232
+ class UsersController < ApplicationController
233
+ include Boosted::Controllers::Searchable
234
+
235
+ # This will use the query parameter `term` instead of `q`
236
+ # and the search scope `search_by_word` instead of the default
237
+ searchable_by :search_by_word, param: :term
238
+
239
+ def index
240
+ users = search(User.all)
241
+ render json: users
242
+ end
243
+ end
244
+ ```
245
+
246
+ #### Boosted::Controllers::Sortable
247
+
248
+ The `Sortable` concern provides a method to sort the records in a controller's action.
249
+
250
+ The method `sortable_by` is used to define the sortable fields.
251
+
252
+ ```ruby
253
+ class UsersController < ApplicationController
254
+ include Boosted::Controllers::Sortable
255
+
256
+ sortable_by :name, :created_at
257
+
258
+ def index
259
+ users = sort(User.all)
260
+ render json: users
261
+ end
262
+ end
263
+ ```
264
+
265
+ This will use the query parameter `sort_key` and `sort_direction` to sort the records.
266
+ - The default sort direction is `desc`.
267
+ - The default sort key is `:id`.
268
+
269
+ Example requests:
270
+ ```
271
+ GET /users?sort_key=name # sort by name in descending order
272
+ GET /users?sort_key=created_at&sort_direction=asc # sort by created_at in ascending order
273
+ ```
274
+
275
+ When calling sort in a controller action, and the sort parameters are not provided, the default sort key and direction will be used.
276
+
277
+ ```ruby
278
+ class UsersController < ApplicationController
279
+ include Boosted::Controllers::Sortable
280
+
281
+ sortable_by :name, :created_at
282
+
283
+ def index
284
+ users = sort(User.all)
285
+ render json: users
286
+ end
287
+ end
288
+ ```
289
+
290
+ Request examples:
291
+ ```
292
+ GET /users # sort by id in descending order
293
+ ```
294
+ ##### Referencing tables in the sortable fields
295
+
296
+ Like the `filterable_by` method, the `sortable_by` method can also be used to reference fields in associated tables.
297
+
298
+ ```ruby
299
+ class UsersController < ApplicationController
300
+ include Boosted::Controllers::Sortable
301
+
302
+ sortable_by :name, 'companies.name'
303
+
304
+ def index
305
+ users = sort(User.joins(:company))
306
+ render json: users
307
+ end
308
+ end
309
+ ```
310
+
311
+ Request examples:
312
+ ```
313
+ GET /users?sort_key=name # sort by name in descending order
314
+ GET /users?sort_key=companies.name&sort_direction=asc # sort by company name in ascending order
315
+ ```
316
+
317
+ ##### Renaming the sort query parameters
318
+
319
+ If you don't want to use the field name as the query parameter (as to not expose the database schema, or when joining the same table multiple times),
320
+ you can specify the query parameter to use for each field:
321
+
322
+ ```ruby
323
+ class UsersController < ApplicationController
324
+ include Boosted::Controllers::Sortable
325
+
326
+ sortable_by 'companies.name' => :company_name, 'users.name' => :user_name
327
+
328
+ def index
329
+ users = sort(User.join(:company))
330
+ render json: users
331
+ end
332
+ end
333
+ ```
334
+
335
+ Request examples:
336
+ ```
337
+ GET /users?sort_key=user_name # sort by name in descending order
338
+ GET /users?sort_key=company_name&sort_direction=asc # sort by company name in ascending order
339
+ ```
340
+
341
+ #### Boosted::Controllers::Pageable
342
+
343
+ The `Pageable` concern provides a method to paginate the records in a controller's action.
344
+
345
+ The method `paginate` is used to paginate the records.
346
+ It will use the query parameters `page` and `per_page` to paginate the records.
347
+
348
+ ```ruby
349
+ class UsersController < ApplicationController
350
+ include Boosted::Controllers::Pageable
351
+
352
+ def index
353
+ users = paginate(User.all)
354
+ render json: users, meta: page_info
355
+ end
356
+ end
357
+ ```
358
+
359
+ Request examples:
360
+ ```
361
+ GET /users?page=1&per_page=10 # returns the first page of users with 10 records per page
362
+ GET /users?per_page=25 # returns the first page of users with 25 records per page
363
+ GET /users?page=2&per_page=25 # returns the second page of users with 25 records per page
364
+ ```
365
+
366
+ ##### Accessing the pagination information
367
+
368
+ The `page_info` method can be used to access the pagination information.
369
+
370
+ ```ruby
371
+ class UsersController < ApplicationController
372
+ include Boosted::Controllers::Pageable
373
+
374
+ def index
375
+ users = paginate(User.all)
376
+ render json: users, meta: page_info
377
+ end
378
+ end
379
+ ```
380
+
381
+ `page_info` returns a hash with
382
+ - `page` - the current page
383
+ - `per_page` - the number of records per page
384
+ - `total_pages` - the total number of pages
385
+ - `total_count` - the number of records in the scope
386
+ - `next-page` - the next page number
387
+ - `prev-page` - the previous page number
388
+
389
+
390
+ ##### Customizing the pagination behavior
391
+
392
+ By default, the `paginate` method will paginate the scope in pages of size 10, and will return the first page if the `page` query parameter is not provided.
393
+
394
+ Additionally, there's a limit of `100` records per page. So, if the `per_page` query parameter is greater than `100`, the pagination will use `100` as the page size.
395
+
396
+ You can customize the default page size and the default page number by overriding the `default_per_page` value in the controller.
397
+
398
+ ```ruby
399
+ class UsersController < ApplicationController
400
+ include Boosted::Controllers::Pageable
401
+
402
+ # This will set the default page size to 25 when the `per_page` query parameter is not provided
403
+ self.default_per_page = 25
404
+
405
+ def index
406
+ users = paginate(User.all)
407
+ render json: users, meta: page_info
408
+ end
409
+ end
410
+ ```
411
+
412
+ #### Boosted::Controllers::Tabulatable
413
+
414
+ The `Tabulatable` concern provides a method to filter, sort, search, and paginate the records in a controller's action.
415
+
416
+ The method `tabulate` is used to filter, sort, search, and paginate the records. So, in the case that the controller action needs to filter, sort, search, and paginate the records, the `tabulate` method can be used.
417
+
418
+ The tabulatable concern provides the `tabulatable_by` method, which passes the values to `filterable_by` and `sortable_by`.
419
+
420
+ ```ruby
421
+ class UsersController < ApplicationController
422
+ include Boosted::Controllers::Tabulatable
423
+
424
+ tabulatable_by :name, :email, :created_at
425
+
426
+ def index
427
+ users = tabulate(User.all)
428
+ render json: users, meta: page_info
429
+ end
430
+ end
431
+ ```
432
+
433
+ Request examples:
434
+ ```
435
+ GET /users?age[]=18&age[]=30&age.rel=between&sort_key=name&sort_direction=asc&q=John&page=2&per_page=10
436
+ # returns the second page of users with 10 records per page, where the age is between 18 and 30, sorted by name in ascending order, and searched by the term John
437
+ ```
438
+
439
+ Just like `paginate`, when calling the `tabulate` method in the controller action, the `page_info` method can be used to access the pagination information.
440
+
441
+ ### Services
442
+
443
+ The gem provides a `Boosted::Service::Base` class that can be used to create services in a rails application.
444
+
445
+ ```ruby
446
+ class PrintService < Boosted::Service::Base
447
+ def call
448
+ puts 'Hello, world!'
449
+ end
450
+ end
451
+ ```
452
+
453
+ The `call` method is the entry point for the service. It can be overridden to provide the service's functionality.
454
+
455
+ ```ruby
456
+ class PrintService < Boosted::Service::Base
457
+ def call
458
+ puts "Hello, #{name}!"
459
+ end
460
+
461
+ private
462
+
463
+ def name
464
+ 'world'
465
+ end
466
+ end
467
+ ```
468
+
469
+ The way of calling the service is by calling the `call` method on the service class.
470
+ The .call method is a class method that creates a new instance of the service, passing the arguments to the `initialize` method, and then calls the `call` method on the new instance.
471
+
472
+ ```ruby
473
+ PrintService.call # Executes new.call
474
+ ```
475
+
476
+ If you want to pass arguments to the service, you can so by defining the `initialize` method in the service class.
477
+
478
+ ```ruby
479
+ class PrintService < Boosted::Service::Base
480
+ def initialize(name = 'John')
481
+ @name = name
482
+ end
483
+
484
+ def call
485
+ puts "Hello, #{@name}!"
486
+ end
487
+ end
488
+ ```
489
+
490
+ ```ruby
491
+ PrintService.call # Executes new.call, prints "Hello, John!"
492
+ PrintService.call('world') # Executes new('world').call, prints "Hello, world!"
493
+ ```
494
+
495
+ ##### Using services as job classes in the background
496
+
497
+ The `Boosted::Service::Base` class provides a class method `.enable_job!` that can be used to enable the service to be used as a job class.
498
+
499
+ ```ruby
500
+ class PrintService < Boosted::Service::Base
501
+ enable_job!
502
+
503
+ def call
504
+ puts 'Hello, world!'
505
+ end
506
+ end
507
+ ```
508
+
509
+ The `enable_job!` method will define a `PrintService::Job` class that inherits from `Boosted::Jobs::Base` and calls the `call` method on the service instance.
510
+
511
+ ```ruby
512
+ PrintService.call_later # Executes PrintService::Job.perform_later
513
+ PrintService::Job.perform_later # Executes PrintService.new.call in the background
514
+ ```
515
+
516
+ call_later and perform_later will pass the arguments to the `initialize` method of the service class, and then call the `call` method on the new instance.
517
+
518
+ ```ruby
519
+ PrintService.call_later('world') # Executes PrintService::Job.perform_later('world')
520
+ PrintService::Job.perform_later('world') # Executes PrintService.new('world').call in the background
521
+ ```
522
+
523
+ ### Jobs
524
+
525
+ The gem provides a `Boosted::Jobs::Base` class that can be used to create background jobs in a rails application.
526
+
527
+ ```ruby
528
+ class PrintJob < Boosted::Jobs::Base
529
+ def perform
530
+ puts 'Hello, world!'
531
+ end
532
+ end
533
+ ```
534
+
535
+ Boosted::Jobs::Base is a subclass of ActiveJob::Base, and can be used as a regular ActiveJob job.
536
+
537
+ The superclass can be overriden to inherit from a different job class, by changing it in the `config/initializers/boosted.rb` file.
538
+
539
+ ```ruby
540
+ # config/initializers/boosted.rb
541
+ Boosted.configure do |config|
542
+ config.job_superclass = 'ApplicationJob'
543
+ end
544
+ ```
545
+
546
+
547
+ ## Development
548
+
549
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
550
+
551
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
552
+
553
+ ## Contributing
554
+
555
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gogrow-dev/boosted. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/gogrow-dev/boosted/blob/main/CODE_OF_CONDUCT.md).
556
+
557
+ ## License
558
+
559
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
560
+
561
+ ## Code of Conduct
562
+
563
+ Everyone interacting in the Boosted project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/gogrow-dev/boosted/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/boosted.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/boosted/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "boosted-rails"
7
+ spec.version = Boosted::VERSION
8
+ spec.authors = ["Juan Aparicio"]
9
+ spec.email = ["apariciojuan30@gmail.com"]
10
+
11
+ spec.summary = "Set of modules to boost your Ruby on Rails development"
12
+ spec.homepage = "https://github.com/gogrow-dev/boosted"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 3.1.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/gogrow-dev/boosted"
18
+ spec.metadata["rubygems_mfa_required"] = "true"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency "rails", ">= 6.0", "< 8.0"
32
+ spec.add_dependency "zeitwerk", ">= 2.4"
33
+ end