wallaby-core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +31 -0
- data/app/controllers/wallaby/application_controller.rb +84 -0
- data/app/controllers/wallaby/resources_controller.rb +381 -0
- data/app/controllers/wallaby/secure_controller.rb +81 -0
- data/app/security/ability.rb +13 -0
- data/config/locales/wallaby.en.yml +140 -0
- data/config/locales/wallaby_class.en.yml +30 -0
- data/config/routes.rb +39 -0
- data/lib/adaptors/wallaby/custom.rb +7 -0
- data/lib/adaptors/wallaby/custom/default_provider.rb +9 -0
- data/lib/adaptors/wallaby/custom/model_decorator.rb +71 -0
- data/lib/adaptors/wallaby/custom/model_finder.rb +13 -0
- data/lib/adaptors/wallaby/custom/model_pagination_provider.rb +14 -0
- data/lib/adaptors/wallaby/custom/model_service_provider.rb +48 -0
- data/lib/authorizers/wallaby/cancancan_authorization_provider.rb +72 -0
- data/lib/authorizers/wallaby/default_authorization_provider.rb +58 -0
- data/lib/authorizers/wallaby/model_authorizer.rb +100 -0
- data/lib/authorizers/wallaby/pundit_authorization_provider.rb +89 -0
- data/lib/concerns/wallaby/authorizable.rb +103 -0
- data/lib/concerns/wallaby/baseable.rb +36 -0
- data/lib/concerns/wallaby/decoratable.rb +101 -0
- data/lib/concerns/wallaby/defaultable.rb +38 -0
- data/lib/concerns/wallaby/engineable.rb +61 -0
- data/lib/concerns/wallaby/fieldable.rb +78 -0
- data/lib/concerns/wallaby/paginatable.rb +72 -0
- data/lib/concerns/wallaby/rails_overridden_methods.rb +42 -0
- data/lib/concerns/wallaby/resourcable.rb +149 -0
- data/lib/concerns/wallaby/servicable.rb +68 -0
- data/lib/concerns/wallaby/shared_helpers.rb +22 -0
- data/lib/concerns/wallaby/themeable.rb +40 -0
- data/lib/decorators/wallaby/resource_decorator.rb +189 -0
- data/lib/errors/wallaby/cell_handling.rb +6 -0
- data/lib/errors/wallaby/forbidden.rb +6 -0
- data/lib/errors/wallaby/general_error.rb +6 -0
- data/lib/errors/wallaby/invalid_error.rb +6 -0
- data/lib/errors/wallaby/model_not_found.rb +11 -0
- data/lib/errors/wallaby/not_authenticated.rb +6 -0
- data/lib/errors/wallaby/not_found.rb +6 -0
- data/lib/errors/wallaby/not_implemented.rb +6 -0
- data/lib/errors/wallaby/resource_not_found.rb +11 -0
- data/lib/errors/wallaby/unprocessable_entity.rb +6 -0
- data/lib/forms/wallaby/form_builder.rb +60 -0
- data/lib/helpers/wallaby/application_helper.rb +79 -0
- data/lib/helpers/wallaby/base_helper.rb +65 -0
- data/lib/helpers/wallaby/configuration_helper.rb +18 -0
- data/lib/helpers/wallaby/form_helper.rb +62 -0
- data/lib/helpers/wallaby/index_helper.rb +84 -0
- data/lib/helpers/wallaby/links_helper.rb +213 -0
- data/lib/helpers/wallaby/resources_helper.rb +52 -0
- data/lib/helpers/wallaby/secure_helper.rb +54 -0
- data/lib/helpers/wallaby/styling_helper.rb +82 -0
- data/lib/interfaces/wallaby/mode.rb +72 -0
- data/lib/interfaces/wallaby/model_authorization_provider.rb +99 -0
- data/lib/interfaces/wallaby/model_decorator.rb +168 -0
- data/lib/interfaces/wallaby/model_finder.rb +12 -0
- data/lib/interfaces/wallaby/model_pagination_provider.rb +107 -0
- data/lib/interfaces/wallaby/model_service_provider.rb +84 -0
- data/lib/paginators/wallaby/model_paginator.rb +115 -0
- data/lib/paginators/wallaby/resource_paginator.rb +12 -0
- data/lib/parsers/wallaby/parser.rb +34 -0
- data/lib/renderers/wallaby/cell.rb +137 -0
- data/lib/renderers/wallaby/cell_resolver.rb +89 -0
- data/lib/renderers/wallaby/custom_lookup_context.rb +64 -0
- data/lib/renderers/wallaby/custom_partial_renderer.rb +33 -0
- data/lib/renderers/wallaby/custom_renderer.rb +16 -0
- data/lib/responders/wallaby/json_api_responder.rb +101 -0
- data/lib/responders/wallaby/resources_responder.rb +28 -0
- data/lib/routes/wallaby/resources_router.rb +72 -0
- data/lib/servicers/wallaby/model_servicer.rb +154 -0
- data/lib/services/wallaby/engine_name_finder.rb +22 -0
- data/lib/services/wallaby/engine_url_for.rb +46 -0
- data/lib/services/wallaby/link_options_normalizer.rb +19 -0
- data/lib/services/wallaby/map/mode_mapper.rb +27 -0
- data/lib/services/wallaby/map/model_class_collector.rb +49 -0
- data/lib/services/wallaby/map/model_class_mapper.rb +38 -0
- data/lib/services/wallaby/prefixes_builder.rb +66 -0
- data/lib/services/wallaby/sorting/hash_builder.rb +19 -0
- data/lib/services/wallaby/sorting/link_builder.rb +69 -0
- data/lib/services/wallaby/sorting/next_builder.rb +63 -0
- data/lib/services/wallaby/sorting/single_builder.rb +20 -0
- data/lib/services/wallaby/type_renderer.rb +50 -0
- data/lib/support/action_dispatch/routing/mapper.rb +75 -0
- data/lib/tree/wallaby/node.rb +25 -0
- data/lib/utils/wallaby/cell_utils.rb +34 -0
- data/lib/utils/wallaby/field_utils.rb +43 -0
- data/lib/utils/wallaby/filter_utils.rb +20 -0
- data/lib/utils/wallaby/model_utils.rb +51 -0
- data/lib/utils/wallaby/module_utils.rb +46 -0
- data/lib/utils/wallaby/params_utils.rb +14 -0
- data/lib/utils/wallaby/preload_utils.rb +44 -0
- data/lib/utils/wallaby/test_utils.rb +34 -0
- data/lib/utils/wallaby/utils.rb +27 -0
- data/lib/wallaby/configuration.rb +103 -0
- data/lib/wallaby/configuration/features.rb +24 -0
- data/lib/wallaby/configuration/mapping.rb +140 -0
- data/lib/wallaby/configuration/metadata.rb +23 -0
- data/lib/wallaby/configuration/models.rb +46 -0
- data/lib/wallaby/configuration/pagination.rb +30 -0
- data/lib/wallaby/configuration/security.rb +98 -0
- data/lib/wallaby/configuration/sorting.rb +28 -0
- data/lib/wallaby/constants.rb +45 -0
- data/lib/wallaby/core.rb +117 -0
- data/lib/wallaby/core/version.rb +7 -0
- data/lib/wallaby/engine.rb +43 -0
- data/lib/wallaby/map.rb +170 -0
- metadata +222 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
# Service object to find the engine name by given request environment variables.
|
5
|
+
class EngineNameFinder
|
6
|
+
class << self
|
7
|
+
# Use script name to find out the corresponding named route and its engine name.
|
8
|
+
#
|
9
|
+
# When it can't find the engine name, it will return empty string. Reason is to prevent it from being run again.
|
10
|
+
# @param env [Hash, String] request env or script name
|
11
|
+
# @return [String] engine name or empty string if not found
|
12
|
+
def find(env)
|
13
|
+
script_name = env[SCRIPT_NAME] || env[PATH_INFO] if env.is_a? Hash
|
14
|
+
script_name = env if env.is_a? String
|
15
|
+
return EMPTY_STRING if script_name.blank?
|
16
|
+
|
17
|
+
named_route = Rails.application.routes.routes.find { |route| route.path.match(script_name) }
|
18
|
+
named_route.try(:name) || EMPTY_STRING
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
# {Wallaby::ApplicationHelper#url_for} helper for Wallaby engine
|
5
|
+
class EngineUrlFor
|
6
|
+
# A constant to map actions to their route paths defined in Wallaby routes.
|
7
|
+
ACTION_TO_PATH_MAP =
|
8
|
+
Wallaby::ERRORS.each_with_object(ActiveSupport::HashWithIndifferentAccess.new) do |error, map|
|
9
|
+
map[error] = :"#{error}_path"
|
10
|
+
end.merge(
|
11
|
+
home: :root_path,
|
12
|
+
index: :resources_path,
|
13
|
+
new: :new_resource_path,
|
14
|
+
show: :resource_path,
|
15
|
+
edit: :edit_resource_path
|
16
|
+
).freeze
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# Generate URL that Wallaby engine supports (e.g. home/resourceful/errors)
|
20
|
+
# @see https://github.com/reinteractive/wallaby/blob/master/config/routes.rb config/routes.rb
|
21
|
+
# @param engine_name [string]
|
22
|
+
# @param parameters [ActionController::Parameters, Hash]
|
23
|
+
# @return [String] path string for wallaby engine
|
24
|
+
# @return [nil] nil if given engine name cannot be found
|
25
|
+
def handle(engine_name:, parameters:)
|
26
|
+
route = Rails.application.routes.named_routes[engine_name]
|
27
|
+
return unless route
|
28
|
+
|
29
|
+
params = { script_name: route.path.spec.to_s }.merge(parameters).symbolize_keys
|
30
|
+
|
31
|
+
ModuleUtils.try_to(
|
32
|
+
Engine.routes.url_helpers, action_path_from(params), params
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
# Find out the named path from given params
|
39
|
+
# @return [Symbol] named path
|
40
|
+
def action_path_from(params)
|
41
|
+
action = params[:action] || params.fetch(:_recall, {})[:action]
|
42
|
+
ACTION_TO_PATH_MAP[action]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
# Link renderer
|
5
|
+
class LinkOptionsNormalizer
|
6
|
+
# Normalize options for `link_to` to use
|
7
|
+
# @param html_options [Hash] HTML options
|
8
|
+
# @param block [Proc] a block
|
9
|
+
# @param defaults [Hash]
|
10
|
+
# @return [Array<Hash, Proc>] html_options and the block
|
11
|
+
def self.normalize(html_options, block, defaults)
|
12
|
+
block ||= defaults[:block]
|
13
|
+
html_options[:title] ||= defaults[:block].call
|
14
|
+
# allow empty class to be set
|
15
|
+
html_options[:class] = defaults[:class] if !html_options.key?(:class) && defaults[:class]
|
16
|
+
[html_options, block]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
class Map
|
5
|
+
# To generate a hash map (`model` => `mode`).
|
6
|
+
# This will be used to tell if a model can be handled by Wallaby
|
7
|
+
class ModeMapper
|
8
|
+
# @param mode_classes [Array<Class>] model classes
|
9
|
+
def initialize(mode_classes)
|
10
|
+
@mode_classes = mode_classes
|
11
|
+
end
|
12
|
+
|
13
|
+
# This will walk through each mode (e.g. **ActiveRecord**/**Her**) then pull out all the models,
|
14
|
+
# and then form a hash of (`model` => `mode`).
|
15
|
+
# @return [Hash] { model_class => mode }
|
16
|
+
def map
|
17
|
+
return {} if @mode_classes.blank?
|
18
|
+
|
19
|
+
@mode_classes.each_with_object({}) do |mode_class, map|
|
20
|
+
mode_class.model_finder.new.all.each do |model_class|
|
21
|
+
map[model_class] = mode_class
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
class Map
|
5
|
+
# To collect model classes that are configured to be handled by Wallaby
|
6
|
+
class ModelClassCollector
|
7
|
+
# @param configuration [Configuration]
|
8
|
+
# @param models [Array<Class>]
|
9
|
+
def initialize(configuration, models = nil)
|
10
|
+
@configuration = configuration
|
11
|
+
@models = models || []
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Array<Class>] model class
|
15
|
+
def collect
|
16
|
+
return @models - excluded_models if configured_models.blank?
|
17
|
+
|
18
|
+
invalid_models_check
|
19
|
+
configured_models
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Check if the models are valid, raise if invalid
|
25
|
+
def invalid_models_check
|
26
|
+
invalid_models = configured_models - @models
|
27
|
+
return if invalid_models.blank?
|
28
|
+
|
29
|
+
message = I18n.t 'errors.invalid.models', models: invalid_models.to_sentence
|
30
|
+
raise InvalidError, message
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Wallaby::Configuration::Models]
|
34
|
+
def models
|
35
|
+
@configuration.models
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Array<Class>] a list of models to exclude
|
39
|
+
def excluded_models
|
40
|
+
models.excludes
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Array<Class>] a list of models to set
|
44
|
+
def configured_models
|
45
|
+
models.presence
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
class Map
|
5
|
+
# Generate a map.
|
6
|
+
class ModelClassMapper
|
7
|
+
# Iterate all classes and generate a hash using their model classes as the key
|
8
|
+
# @see #map
|
9
|
+
# @param class_array [Array<Class>]
|
10
|
+
# @return [Hash] model class => descendant class
|
11
|
+
def self.map(class_array, &block)
|
12
|
+
new.send :map, class_array, &block
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
# @return [Hash] model class => descendant class
|
18
|
+
def map(class_array)
|
19
|
+
(class_array || EMPTY_ARRAY).each_with_object({}) do |klass, map|
|
20
|
+
next if anonymous?(klass) || base_class?(klass) || !klass.model_class
|
21
|
+
|
22
|
+
map[klass.model_class] = block_given? ? yield(klass) : klass
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @see Wallaby::ModuleUtils.anonymous_class?
|
27
|
+
def anonymous?(klass)
|
28
|
+
ModuleUtils.anonymous_class? klass
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param klass [Class]
|
32
|
+
# @return [Boolean] whether the class is base or not
|
33
|
+
def base_class?(klass)
|
34
|
+
ModuleUtils.try_to klass, :base_class?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
# To extend prefixes to provide more possibility
|
5
|
+
class PrefixesBuilder
|
6
|
+
# @param origin_prefixes [Array<string>] a list of all the origin prefixes
|
7
|
+
# @param action_name [String] action name
|
8
|
+
# @param resources_name [String] resources name
|
9
|
+
# @param script_name [String] script name
|
10
|
+
# @param theme_name [String] theme name
|
11
|
+
def self.build(origin_prefixes:, action_name:, resources_name:, script_name:, theme_name:)
|
12
|
+
new(
|
13
|
+
origin_prefixes: origin_prefixes,
|
14
|
+
action_name: action_name,
|
15
|
+
resources_name: resources_name,
|
16
|
+
script_name: script_name,
|
17
|
+
theme_name: theme_name
|
18
|
+
).send :build
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Constructor
|
24
|
+
def initialize(origin_prefixes:, action_name:, resources_name:, script_name:, theme_name:)
|
25
|
+
@origin_prefixes = origin_prefixes
|
26
|
+
@action_name = action_name
|
27
|
+
@resources_name = resources_name
|
28
|
+
@script_name = script_name
|
29
|
+
@theme_name = theme_name
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array<String>] prefixes
|
33
|
+
def build
|
34
|
+
prefixes = minimal_prefixes
|
35
|
+
prefixes.unshift mounted_prefix unless prefixes.include? resources_path
|
36
|
+
prefixes.uniq.compact.each_with_object([]) do |prefix, result|
|
37
|
+
result << "#{prefix}/#{suffix}" << prefix
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Array<String>] prefixes starting from wallaby controller path
|
42
|
+
def minimal_prefixes
|
43
|
+
index = @origin_prefixes.index ResourcesController.controller_path
|
44
|
+
@origin_prefixes.slice(0..index).tap do |prefixes|
|
45
|
+
insert_index = prefixes.length > 1 ? -2 : 0
|
46
|
+
prefixes.insert insert_index, @theme_name if @theme_name.present?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] a prefix of the mouted path
|
51
|
+
def mounted_prefix
|
52
|
+
prefix = (@script_name || '').sub(%r{^/}, '')
|
53
|
+
prefix << SLASH if prefix.present?
|
54
|
+
prefix << resources_path if resources_path
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [String]
|
58
|
+
def suffix
|
59
|
+
@suffix ||= CellUtils.to_action_prefix @action_name
|
60
|
+
end
|
61
|
+
|
62
|
+
def resources_path
|
63
|
+
@resources_path ||= @resources_name.try :gsub, COLONS, SLASH
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
module Sorting
|
5
|
+
# Turn a string e.g.`'name asc,id desc'` into sort hash e.g.`{name: 'asc', id: 'desc'}`
|
6
|
+
class HashBuilder
|
7
|
+
SORT_REGEX = /(([^\s,]+)\s+(asc|desc))\s*,?\s*/i.freeze
|
8
|
+
|
9
|
+
# Turn a string e.g.`'name asc,id desc'` into sort hash e.g.`{name: 'asc', id: 'desc'}`
|
10
|
+
# @param sort_string [String]
|
11
|
+
# @return [Hash] { field_name => 'asc|desc' }
|
12
|
+
def self.build(sort_string)
|
13
|
+
::ActiveSupport::HashWithIndifferentAccess.new.tap do |hash|
|
14
|
+
(sort_string || EMPTY_STRING).scan(SORT_REGEX) { |_, key, order| hash[key] = order }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
module Sorting
|
5
|
+
# Build link for sorting
|
6
|
+
class LinkBuilder
|
7
|
+
SORT_STRATEGIES = { single: SingleBuilder }.with_indifferent_access.freeze
|
8
|
+
|
9
|
+
delegate :model_class, to: :@model_decorator
|
10
|
+
delegate :index_link, :url_for, to: :@helper
|
11
|
+
|
12
|
+
# @param model_decorator [Wallaby::ModelDecorator]
|
13
|
+
# @param params [ActionController::Parameters]
|
14
|
+
# @param helper [ActionView::Helpers]
|
15
|
+
def initialize(model_decorator, params, helper, strategy)
|
16
|
+
@model_decorator = model_decorator
|
17
|
+
@params = params
|
18
|
+
@helper = helper
|
19
|
+
@strategy = strategy
|
20
|
+
end
|
21
|
+
|
22
|
+
# To return the sort hash converted from string value, e.g. `{ 'title' => 'asc', 'updated_at' => 'desc' }`
|
23
|
+
# converted from `'title asc, updated_at desc'`
|
24
|
+
# @return [Hash] current sort hash
|
25
|
+
def current_sort
|
26
|
+
@current_sort ||= HashBuilder.build @params[:sort]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Build sort link for given field name:
|
30
|
+
#
|
31
|
+
# ```
|
32
|
+
# <a title="Product" href="/admin/products?sort=published_at+asc">Name</a>
|
33
|
+
# ```
|
34
|
+
#
|
35
|
+
# If the field is not sortable, it returns a text, e.g.:
|
36
|
+
#
|
37
|
+
# ```
|
38
|
+
# Name
|
39
|
+
# ```
|
40
|
+
# @param field_name [String]
|
41
|
+
# @return [String] link or text
|
42
|
+
def build(field_name)
|
43
|
+
metadata = @model_decorator.index_metadata_of field_name
|
44
|
+
label = @model_decorator.index_label_of field_name
|
45
|
+
return label unless sortable? field_name, metadata
|
46
|
+
|
47
|
+
sort_field_name = metadata[:sort_field_name] || field_name
|
48
|
+
url_params = next_builder.next_params sort_field_name
|
49
|
+
url = url_for url_params.merge(with_query: true)
|
50
|
+
index_link(model_class, options: { url: url }) { label }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# @return [Wallaby::Sorting::NextBuilder]
|
56
|
+
def next_builder
|
57
|
+
@next_builder ||= begin
|
58
|
+
klass = SORT_STRATEGIES[@strategy] || NextBuilder
|
59
|
+
klass.new @params, current_sort
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Boolean] true if sortable or `sort_field_name` is provided in the metadata
|
64
|
+
def sortable?(field_name, metadata)
|
65
|
+
!metadata[:sort_disabled] && (@model_decorator.fields[field_name] || metadata[:sort_field_name])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
module Sorting
|
5
|
+
# Generate sort param for given field's next sort order
|
6
|
+
# (e.g. from empty to `asc`, from `asc` to `desc`, from `desc` to empty)
|
7
|
+
class NextBuilder
|
8
|
+
ASC = 'asc'
|
9
|
+
DESC = 'desc'
|
10
|
+
|
11
|
+
# @param params [ActionController::Parameters]
|
12
|
+
# @param hash [Hash, nil] a hash containing sorting info, e.g. `{ name: 'asc' }`
|
13
|
+
def initialize(params, hash = nil)
|
14
|
+
@params = params
|
15
|
+
@hash = hash.try(:with_indifferent_access) || HashBuilder.build(params[:sort])
|
16
|
+
end
|
17
|
+
|
18
|
+
# Update the `sort` parameter.
|
19
|
+
# @example for param `{sort: 'name asc'}`, it updates the parameters to:
|
20
|
+
# {sort: 'name desc'}
|
21
|
+
# @param field_name [String] field name
|
22
|
+
# @return [ActionController::Parameters]
|
23
|
+
# updated parameters with new sort order for given field
|
24
|
+
def next_params(field_name)
|
25
|
+
params = @params.dup
|
26
|
+
params[:sort] = complete_sorting_str_with field_name
|
27
|
+
params
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
# @param field_name [String] field name
|
33
|
+
# @return [String] a sort order string, e.g. `'name asc'`
|
34
|
+
def complete_sorting_str_with(field_name)
|
35
|
+
hash = @hash.except field_name
|
36
|
+
current_sort = @hash[field_name]
|
37
|
+
hash[field_name] = next_value_for current_sort
|
38
|
+
rebuild_str_from hash
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param hash [Hash] sort order hash
|
42
|
+
# @return [String] a sort order string, e.g. `'name asc'`
|
43
|
+
def rebuild_str_from(hash)
|
44
|
+
hash.each_with_object(EMPTY_STRING.dup) do |(name, sort), str|
|
45
|
+
next unless sort
|
46
|
+
|
47
|
+
str << (str == EMPTY_STRING ? str : COMMA)
|
48
|
+
str << name.to_s << SPACE << sort
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param current [String, nil] current sort order
|
53
|
+
# @return [String, nil] next sort order
|
54
|
+
def next_value_for(current)
|
55
|
+
case current
|
56
|
+
when ASC then DESC
|
57
|
+
when DESC then nil
|
58
|
+
else ASC
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wallaby
|
4
|
+
module Sorting
|
5
|
+
# Generate sort param for only one field's next sort order
|
6
|
+
# (e.g. from empty to `asc`, from `asc` to `desc`, from `desc` to empty)
|
7
|
+
class SingleBuilder < NextBuilder
|
8
|
+
protected
|
9
|
+
|
10
|
+
# @param field_name [String] field name
|
11
|
+
# @return [String] a sort order string, e.g. `'name asc'`
|
12
|
+
def complete_sorting_str_with(field_name)
|
13
|
+
hash = {}
|
14
|
+
current_sort = @hash[field_name]
|
15
|
+
hash[field_name] = next_value_for current_sort
|
16
|
+
rebuild_str_from hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|