Skip to content

Commit

Permalink
Add UI controller concerns for fullstack rails (#7)
Browse files Browse the repository at this point in the history
* add UI controller concerns for fullstack rails apps

* add UI modules

* fix offenses and specs
  • Loading branch information
juan-apa authored Apr 5, 2024
1 parent b8bb8e1 commit 02b8d6a
Show file tree
Hide file tree
Showing 19 changed files with 314 additions and 46 deletions.
4 changes: 3 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ Layout/LineLength:

Metrics/AbcSize:
Exclude:
- "lib/warped/queries/filter.rb"
- "lib/warped/controllers/filterable/ui.rb"
- "lib/warped/controllers/sortable/ui.rb"
- "lib/warped/emails/components/**/*.rb"
- "lib/warped/queries/filter.rb"

Metrics/BlockLength:
Exclude:
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class UsersController < ApplicationController

def index
users = paginate(User.all)
render json: users, meta: page_info
render json: users, meta: pagination
end
end
```
Expand Down Expand Up @@ -189,7 +189,7 @@ class UsersController < ApplicationController

def index
users = tabulate(User.all)
render json: users, meta: page_info
render json: users, meta: pagination
end
end
```
Expand All @@ -200,7 +200,7 @@ GET /users?age[]=18&age[]=30&age.rel=between&sort_key=name&sort_direction=asc&q=
# 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
```

Just like `paginate`, when calling the `tabulate` method in the controller action, the `page_info` method can be used to access the pagination information.
Just like `paginate`, when calling the `tabulate` method in the controller action, the `pagination` method can be used to access the pagination information.

[Complete documentation for Warped::Controllers::Tabulatable](docs/controllers/TABULATABLE.md).

Expand Down
10 changes: 5 additions & 5 deletions docs/controllers/PAGEABLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class UsersController < ApplicationController

def index
users = paginate(User.all)
render json: users, meta: page_info
render json: users, meta: pagination
end
end
```
Expand All @@ -25,20 +25,20 @@ GET /users?page=2&per_page=25 # returns the second page of users with 25 records

## Accessing the pagination information

The `page_info` method can be used to access the pagination information.
The `pagination` method can be used to access the pagination information.

```ruby
class UsersController < ApplicationController
include Warped::Controllers::Pageable

def index
users = paginate(User.all)
render json: users, meta: page_info
render json: users, meta: pagination
end
end
```

`page_info` returns a hash with
`pagination` returns a hash with
- `page` - the current page
- `per_page` - the number of records per page
- `total_pages` - the total number of pages
Expand All @@ -64,7 +64,7 @@ class UsersController < ApplicationController

def index
users = paginate(User.all)
render json: users, meta: page_info
render json: users, meta: pagination
end
end
```
4 changes: 2 additions & 2 deletions docs/controllers/TABULATABLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class UsersController < ApplicationController

def index
users = tabulate(User.all)
render json: users, meta: page_info
render json: users, meta: pagination
end
end
```
Expand All @@ -25,4 +25,4 @@ GET /users?age[]=18&age[]=30&age.rel=between&sort_key=name&sort_direction=asc&q=
# 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
```

Just like `paginate`, when calling the `tabulate` method in the controller action, the `page_info` method can be used to access the pagination information.
Just like `paginate`, when calling the `tabulate` method in the controller action, the `pagination` method can be used to access the pagination information.
2 changes: 1 addition & 1 deletion lib/generators/warped/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Generators
class InstallGenerator < Rails::Generators::Base
source_root File.expand_path("templates", __dir__)

def say_hello
def install
template "initializer.rb.tt", "config/initializers/warped.rb"
end
end
Expand Down
4 changes: 4 additions & 0 deletions lib/warped/controllers/filterable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ def filter_conditions(*fields)
end
end

def filterable_by
@filterable_by ||= self.class.filter_fields.concat(self.class.mapped_filter_fields)
end

private

def filter_name(filter)
Expand Down
68 changes: 68 additions & 0 deletions lib/warped/controllers/filterable/ui.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

require "active_support/concern"

module Warped
module Controllers
module Filterable
module Ui
extend ActiveSupport::Concern
include Filterable

included do
helper_method :filters, :filtered?, :current_filters, :filter_url_params, :filterable_by
end

def filter(...)
@filtered = true

super
end

def filtered?
@filtered ||= false
end

def filter_url_params(**options)
url_params = {}
current_filters.each_with_object(url_params) do |filter, hsh|
if filter[:value].is_a?(Array)
filter[:value].each { |value| hsh["#{filter[:name]}[]"] = value }
else
hsh[filter[:name]] = filter[:value]
end

hsh["#{filter[:name]}.rel"] = filter[:relation]
end

url_params.tap { _1.merge!(options) }
end

def filters
(filter_fields | mapped_filter_fields).map do |field|
{
name: filter_mapped_name(field),
value: filter_value(field),
relation: filter_rel_value(field)
}
end
end

def current_filters
(filter_fields | mapped_filter_fields).filter_map do |field|
filter_value = filter_value(field)
filter_rel_value = filter_rel_value(field)

next if filter_value.blank? && %w[is_null is_not_null].exclude?(filter_rel_value)

{
name: filter_mapped_name(field),
value: filter_value,
relation: filter_rel_value
}
end
end
end
end
end
end
20 changes: 10 additions & 10 deletions lib/warped/controllers/pageable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module Controllers
#
# def index
# scope = paginate(User.all)
# render json: scope, root: :users, meta: page_info
# render json: scope, root: :users, meta: pagination
# end
# end
#
Expand All @@ -33,7 +33,7 @@ module Controllers
#
# def index
# scope = paginate(User.all)
# render json: scope, root: :users, meta: page_info
# render json: scope, root: :users, meta: pagination
# end
# end
#
Expand All @@ -44,7 +44,7 @@ module Controllers
#
# def index
# scope = paginate(User.all)
# render json: scope, root: :users, meta: page_info
# render json: scope, root: :users, meta: pagination
# end
#
# private
Expand All @@ -62,25 +62,25 @@ module Controllers
#
# def index
# scope = paginate(User.all, per_page: 50)
# render json: scope, root: :users, meta: page_info
# render json: scope, root: :users, meta: pagination
# end
#
# def other_index
# # The default per_page value is used.
# scope = paginate(User.all)
# render json: scope, root: :users, meta: page_info
# render json: scope, root: :users, meta: pagination
# end
# end
#
# The pagination metadata can be accessed by calling the +page_info+ method.
# The pagination metadata can be accessed by calling the +pagination+ method.
# It includes the following keys:
# - +total_count+: The total number of records in the collection.
# - +total_pages+: The total number of pages.
# - +next_page+: The next page number.
# - +prev_page+: The previous page number.
# - +page+: The current page number.
# - +per_page+: The number of records per page.
# *Warning*: The +page_info+ method will raise an +ArgumentError+ if the method +paginate+ was not
# *Warning*: The +pagination+ method will raise an +ArgumentError+ if the method +paginate+ was not
# called within the action.
module Pageable
extend ActiveSupport::Concern
Expand All @@ -96,7 +96,7 @@ module Pageable
# @param per_page [String,Integer,nil] The number of records per page.
# @return [ActiveRecord::Relation] The paginated scope.
def paginate(scope, page: self.page, per_page: self.per_page)
@page_info, paginated_scope = Queries::Paginate.call(scope, page:, per_page:)
@pagination, paginated_scope = Queries::Paginate.call(scope, page:, per_page:)
paginated_scope
end

Expand All @@ -120,8 +120,8 @@ def per_page
# @return [Hash] Metadata about the pagination.
# @raise [ArgumentError] If pagination was not performed.
# @see Warped::Queries::Paginate#metadata
def page_info
return @page_info if @page_info.present?
def pagination
return @pagination if @pagination.present?

raise ActionController::BadRequest, "Pagination was not performed"
end
Expand Down
59 changes: 59 additions & 0 deletions lib/warped/controllers/pageable/ui.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# frozen_string_literal: true

require "active_support/concern"

module Warped
module Controllers
module Pageable
module Ui
extend ActiveSupport::Concern

include Pageable

included do
helper_method :pagination, :paginated?, :paginate_url_params
end

def paginate_url_params(**options)
url_params = { page:, per_page: }
url_params.merge!(options)
url_params
end

def pagination
super.tap do |hsh|
hsh[:series] = series(hsh[:page], hsh[:total_pages])
end
end

def paginate(...)
@paginated = true

super
end

def paginated?
@paginated ||= false
end

private

# @param page [Integer]
# @param total_pages [Integer]
# @return [Array]
# the series method returns an array of page numbers and :gap symbols
# the current page is a string, the others are integers
# series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
def series(page, total_pages)
return ["1"] if total_pages == 1

if total_pages <= 9
(1..total_pages).to_a
else
[1, :gap, page - 2, page - 1, page.to_s, page + 1, page + 2, :gap, total_pages]
end
end
end
end
end
end
2 changes: 2 additions & 0 deletions lib/warped/controllers/searchable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ module Searchable
included do
class_attribute :model_search_scope, default: :search
class_attribute :search_param, default: :q

helper_method :search_term, :search_param, :model_search_scope
end

class_methods do
Expand Down
35 changes: 35 additions & 0 deletions lib/warped/controllers/searchable/ui.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

require "active_support/concern"

module Warped
module Controllers
module Searchable
module Ui
extend ActiveSupport::Concern

include Searchable

included do
helper_method :searched?, :search_url_params
end

def search(...)
@searched = true

super
end

def searched?
@searched ||= false
end

def search_url_params(**options)
url_params = { search_param => search_term }
url_params.merge!(options)
url_params
end
end
end
end
end
4 changes: 2 additions & 2 deletions lib/warped/controllers/sortable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def sort_direction
def sort_key
@sort_key ||= mapped_sort_fields.key(params[:sort_key]).presence ||
params[:sort_key] ||
default_sort_key
default_sort_key.to_s
end

private
Expand All @@ -106,7 +106,7 @@ def validate_sort_key!
end

def valid_sort_key?
sort_key == default_sort_key ||
sort_key == default_sort_key.to_s ||
sort_fields.include?(sort_key) ||
mapped_sort_fields[sort_key].present?
end
Expand Down
Loading

0 comments on commit 02b8d6a

Please sign in to comment.