wallaby-core 0.1.0

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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +31 -0
  4. data/app/controllers/wallaby/application_controller.rb +84 -0
  5. data/app/controllers/wallaby/resources_controller.rb +381 -0
  6. data/app/controllers/wallaby/secure_controller.rb +81 -0
  7. data/app/security/ability.rb +13 -0
  8. data/config/locales/wallaby.en.yml +140 -0
  9. data/config/locales/wallaby_class.en.yml +30 -0
  10. data/config/routes.rb +39 -0
  11. data/lib/adaptors/wallaby/custom.rb +7 -0
  12. data/lib/adaptors/wallaby/custom/default_provider.rb +9 -0
  13. data/lib/adaptors/wallaby/custom/model_decorator.rb +71 -0
  14. data/lib/adaptors/wallaby/custom/model_finder.rb +13 -0
  15. data/lib/adaptors/wallaby/custom/model_pagination_provider.rb +14 -0
  16. data/lib/adaptors/wallaby/custom/model_service_provider.rb +48 -0
  17. data/lib/authorizers/wallaby/cancancan_authorization_provider.rb +72 -0
  18. data/lib/authorizers/wallaby/default_authorization_provider.rb +58 -0
  19. data/lib/authorizers/wallaby/model_authorizer.rb +100 -0
  20. data/lib/authorizers/wallaby/pundit_authorization_provider.rb +89 -0
  21. data/lib/concerns/wallaby/authorizable.rb +103 -0
  22. data/lib/concerns/wallaby/baseable.rb +36 -0
  23. data/lib/concerns/wallaby/decoratable.rb +101 -0
  24. data/lib/concerns/wallaby/defaultable.rb +38 -0
  25. data/lib/concerns/wallaby/engineable.rb +61 -0
  26. data/lib/concerns/wallaby/fieldable.rb +78 -0
  27. data/lib/concerns/wallaby/paginatable.rb +72 -0
  28. data/lib/concerns/wallaby/rails_overridden_methods.rb +42 -0
  29. data/lib/concerns/wallaby/resourcable.rb +149 -0
  30. data/lib/concerns/wallaby/servicable.rb +68 -0
  31. data/lib/concerns/wallaby/shared_helpers.rb +22 -0
  32. data/lib/concerns/wallaby/themeable.rb +40 -0
  33. data/lib/decorators/wallaby/resource_decorator.rb +189 -0
  34. data/lib/errors/wallaby/cell_handling.rb +6 -0
  35. data/lib/errors/wallaby/forbidden.rb +6 -0
  36. data/lib/errors/wallaby/general_error.rb +6 -0
  37. data/lib/errors/wallaby/invalid_error.rb +6 -0
  38. data/lib/errors/wallaby/model_not_found.rb +11 -0
  39. data/lib/errors/wallaby/not_authenticated.rb +6 -0
  40. data/lib/errors/wallaby/not_found.rb +6 -0
  41. data/lib/errors/wallaby/not_implemented.rb +6 -0
  42. data/lib/errors/wallaby/resource_not_found.rb +11 -0
  43. data/lib/errors/wallaby/unprocessable_entity.rb +6 -0
  44. data/lib/forms/wallaby/form_builder.rb +60 -0
  45. data/lib/helpers/wallaby/application_helper.rb +79 -0
  46. data/lib/helpers/wallaby/base_helper.rb +65 -0
  47. data/lib/helpers/wallaby/configuration_helper.rb +18 -0
  48. data/lib/helpers/wallaby/form_helper.rb +62 -0
  49. data/lib/helpers/wallaby/index_helper.rb +84 -0
  50. data/lib/helpers/wallaby/links_helper.rb +213 -0
  51. data/lib/helpers/wallaby/resources_helper.rb +52 -0
  52. data/lib/helpers/wallaby/secure_helper.rb +54 -0
  53. data/lib/helpers/wallaby/styling_helper.rb +82 -0
  54. data/lib/interfaces/wallaby/mode.rb +72 -0
  55. data/lib/interfaces/wallaby/model_authorization_provider.rb +99 -0
  56. data/lib/interfaces/wallaby/model_decorator.rb +168 -0
  57. data/lib/interfaces/wallaby/model_finder.rb +12 -0
  58. data/lib/interfaces/wallaby/model_pagination_provider.rb +107 -0
  59. data/lib/interfaces/wallaby/model_service_provider.rb +84 -0
  60. data/lib/paginators/wallaby/model_paginator.rb +115 -0
  61. data/lib/paginators/wallaby/resource_paginator.rb +12 -0
  62. data/lib/parsers/wallaby/parser.rb +34 -0
  63. data/lib/renderers/wallaby/cell.rb +137 -0
  64. data/lib/renderers/wallaby/cell_resolver.rb +89 -0
  65. data/lib/renderers/wallaby/custom_lookup_context.rb +64 -0
  66. data/lib/renderers/wallaby/custom_partial_renderer.rb +33 -0
  67. data/lib/renderers/wallaby/custom_renderer.rb +16 -0
  68. data/lib/responders/wallaby/json_api_responder.rb +101 -0
  69. data/lib/responders/wallaby/resources_responder.rb +28 -0
  70. data/lib/routes/wallaby/resources_router.rb +72 -0
  71. data/lib/servicers/wallaby/model_servicer.rb +154 -0
  72. data/lib/services/wallaby/engine_name_finder.rb +22 -0
  73. data/lib/services/wallaby/engine_url_for.rb +46 -0
  74. data/lib/services/wallaby/link_options_normalizer.rb +19 -0
  75. data/lib/services/wallaby/map/mode_mapper.rb +27 -0
  76. data/lib/services/wallaby/map/model_class_collector.rb +49 -0
  77. data/lib/services/wallaby/map/model_class_mapper.rb +38 -0
  78. data/lib/services/wallaby/prefixes_builder.rb +66 -0
  79. data/lib/services/wallaby/sorting/hash_builder.rb +19 -0
  80. data/lib/services/wallaby/sorting/link_builder.rb +69 -0
  81. data/lib/services/wallaby/sorting/next_builder.rb +63 -0
  82. data/lib/services/wallaby/sorting/single_builder.rb +20 -0
  83. data/lib/services/wallaby/type_renderer.rb +50 -0
  84. data/lib/support/action_dispatch/routing/mapper.rb +75 -0
  85. data/lib/tree/wallaby/node.rb +25 -0
  86. data/lib/utils/wallaby/cell_utils.rb +34 -0
  87. data/lib/utils/wallaby/field_utils.rb +43 -0
  88. data/lib/utils/wallaby/filter_utils.rb +20 -0
  89. data/lib/utils/wallaby/model_utils.rb +51 -0
  90. data/lib/utils/wallaby/module_utils.rb +46 -0
  91. data/lib/utils/wallaby/params_utils.rb +14 -0
  92. data/lib/utils/wallaby/preload_utils.rb +44 -0
  93. data/lib/utils/wallaby/test_utils.rb +34 -0
  94. data/lib/utils/wallaby/utils.rb +27 -0
  95. data/lib/wallaby/configuration.rb +103 -0
  96. data/lib/wallaby/configuration/features.rb +24 -0
  97. data/lib/wallaby/configuration/mapping.rb +140 -0
  98. data/lib/wallaby/configuration/metadata.rb +23 -0
  99. data/lib/wallaby/configuration/models.rb +46 -0
  100. data/lib/wallaby/configuration/pagination.rb +30 -0
  101. data/lib/wallaby/configuration/security.rb +98 -0
  102. data/lib/wallaby/configuration/sorting.rb +28 -0
  103. data/lib/wallaby/constants.rb +45 -0
  104. data/lib/wallaby/core.rb +117 -0
  105. data/lib/wallaby/core/version.rb +7 -0
  106. data/lib/wallaby/engine.rb +43 -0
  107. data/lib/wallaby/map.rb +170 -0
  108. metadata +222 -0
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Wallaby application helper
5
+ module ApplicationHelper
6
+ include ConfigurationHelper
7
+ include Engineable
8
+ include SharedHelpers
9
+
10
+ # @!method try_to(subject, method_id, *args, &block)
11
+ # (see Wallaby::ModuleUtils.try_to)
12
+ # @see Wallaby::ModuleUtils.try_to
13
+ delegate :try_to, to: ModuleUtils
14
+
15
+ # Override the origin view_renderer to support {Wallaby::Cell} rendering
16
+ # @!attribute [r] view_renderer
17
+ # @see Wallaby::CustomRenderer
18
+ def view_renderer
19
+ return @view_renderer if @view_renderer.is_a? CustomRenderer
20
+
21
+ @view_renderer = CustomRenderer.new @view_renderer.lookup_context
22
+ end
23
+
24
+ # Override origin method to handle URL for Wallaby engine.
25
+ #
26
+ # As Wallaby's routes are declared in a
27
+ # {https://guides.rubyonrails.org/routing.html#routing-to-rack-applications Rack application} fashion, this will
28
+ # lead to **ActionController::RoutingError** exception when using ordinary **url_for**
29
+ # (e.g. `url_for action: :index`).
30
+ #
31
+ # Gotcha: Wallaby can't cope well with the following situation.
32
+ # It's due to the limit of route declaration and matching:
33
+ #
34
+ # ```
35
+ # scope path: '/prefix' do
36
+ # wresources :products, controller: 'wallaby/resources'
37
+ # end
38
+ # ```
39
+ # @param options [String, Hash, ActionController::Parameters]
40
+ # @option options [Boolean]
41
+ # :with_query to include `request.query_parameters` values for url generation.
42
+ # @option options [String]
43
+ # :engine_name to specify the engine_name to use, default to {Wallaby::Engineable#current_engine_name}
44
+ # @return [String] URL string
45
+ # @see Wallaby::EngineUrlFor.handle
46
+ # @see https://api.rubyonrails.org/classes/ActionView/RoutingUrlFor.html#method-i-url_for
47
+ # ActionView::RoutingUrlFor#url_for
48
+ def url_for(options = nil)
49
+ if options.is_a?(Hash) || try_to(options, :permitted?)
50
+ # merge with all current query parameters
51
+ options = request.query_parameters.merge(options) if options.delete(:with_query)
52
+ options = ParamsUtils.presence url_options, options # remove blank values
53
+ EngineUrlFor.handle(
54
+ engine_name: options.fetch(:engine_name, current_engine_name), parameters: options
55
+ )
56
+ end || super(options)
57
+ end
58
+
59
+ # Override origin method to add turbolinks tracking when it's enabled
60
+ # @param sources [Array<String>]
61
+ # @return [String] stylesheet link tags HTML
62
+ def stylesheet_link_tag(*sources)
63
+ default_options =
64
+ features.turbolinks_enabled ? { 'data-turbolinks-track' => true } : {}
65
+ options = default_options.merge!(sources.extract_options!.stringify_keys)
66
+ super(*sources, options)
67
+ end
68
+
69
+ # Override origin method to add turbolinks tracking when it's enabled
70
+ # @param sources [Array<String>]
71
+ # @return [String] javascript script tags HTML
72
+ def javascript_include_tag(*sources)
73
+ default_options =
74
+ features.turbolinks_enabled ? { 'data-turbolinks-track' => true, 'data-turbolinks-eval' => false } : {}
75
+ options = default_options.merge!(sources.extract_options!.stringify_keys)
76
+ super(*sources, options)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # NOTE: Global helper methods should go in here
5
+ module BaseHelper
6
+ include StylingHelper
7
+ include LinksHelper
8
+
9
+ # @see ModelUtils.to_model_label
10
+ # @return [String] label for given model class
11
+ def to_model_label(model_class)
12
+ ModelUtils.to_model_label model_class
13
+ end
14
+
15
+ # @see Map.resources_name_map
16
+ # @return [String] resources name for given model class
17
+ def to_resources_name(model_class)
18
+ Map.resources_name_map model_class
19
+ end
20
+
21
+ # Generate body class from the following sources:
22
+ # - `:action` parameter
23
+ # - converted current resources name (e.g. `order__item` from `Order::Item`)
24
+ # - `:custom_body_class` content
25
+ # @return [String] css classes for body tag
26
+ def body_class
27
+ [
28
+ params[:action],
29
+ controller_path.gsub(SLASH, '__'),
30
+ current_resources_name.try(:gsub, COLONS, '__'),
31
+ content_for(:custom_body_class)
32
+ ].compact.join SPACE
33
+ end
34
+
35
+ # Turn a list of classes into tree structure by inheritance.
36
+ # @param classes [Array<Class>]
37
+ # a list of all the classes that wallaby supports
38
+ # @return [Array<Wallaby::Node>] a tree structure of given classes
39
+ def model_classes(classes = Map.model_classes)
40
+ nested_hash = classes.each_with_object({}) do |klass, hash|
41
+ hash[klass] = Node.new(klass)
42
+ end
43
+ nested_hash.each do |klass, node|
44
+ node.parent = parent = nested_hash[klass.superclass]
45
+ parent.children << node if parent
46
+ end
47
+ nested_hash.values.select { |v| v.parent.nil? }
48
+ end
49
+
50
+ # Turn the tree of classes into a nested `ul` list.
51
+ # @param array [Array<Wallaby::Node>] root classes
52
+ # @return [String] HTML for the whole tree
53
+ def model_tree(array, base_class = nil)
54
+ return EMPTY_STRING if array.blank?
55
+
56
+ options = { html_options: { class: 'dropdown-item' } }
57
+ content_tag :ul, class: 'dropdown-menu', 'aria-labelledby': base_class do
58
+ array.sort_by(&:name).each do |node|
59
+ content = index_link(node.klass, options).try :<<, model_tree(node.children)
60
+ concat content_tag(:li, content)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Configuration helper module. Provide shortcut methods to configurations.
5
+ module ConfigurationHelper
6
+ delegate :models, :security, :mapping, :pagination, :features, :sorting, to: :configuration
7
+
8
+ # @return [Wallaby::Configuration] shortcut method of configuration
9
+ def configuration
10
+ Wallaby.configuration
11
+ end
12
+
13
+ # @return [Wallaby::Configuration::Metadata] shortcut method of metadata
14
+ def default_metadata
15
+ configuration.metadata
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Form helper
5
+ module FormHelper
6
+ # @deprecated Use {Wallaby::ResourcesHelper#type_render} instead. It will be removed from 5.3.*
7
+ def form_type_partial_render(options = {}, locals = {}, &block)
8
+ Utils.deprecate 'deprecation.form_type_partial_render', caller: caller
9
+ type_render options, locals, &block
10
+ end
11
+
12
+ # To generate remote URL for auto select plugin.
13
+ # @see https://github.com/reinteractive/wallaby/blob/master/app/assets/javascripts/wallaby/auto_select.js
14
+ # auto_select.js
15
+ # @param url [String, nil]
16
+ # if URL is nil, it will fall back to default remote URL
17
+ # @param model_class [Class]
18
+ # @param wildcard [String] wildcard that auto_select uses to replace with
19
+ # the typed keyword
20
+ # @return [String] URL for autocomplete
21
+ def remote_url(url, model_class, wildcard = 'QUERY')
22
+ url || index_path(model_class, url_params: { q: wildcard, per: pagination.page_size })
23
+ end
24
+
25
+ # To generate dropdown options (class => url) for polymorphic class.
26
+ # @see https://github.com/reinteractive/wallaby/blob/master/app/assets/javascripts/wallaby/auto_select.js
27
+ # auto_select.js
28
+ # This function will pull out remote URLs from `metadata[:remote_urls]`
29
+ # (Class => url).
30
+ # @see ActionView::Helpers::FormOptionsHelper#options_for_select
31
+ # @param metadata [Hash]
32
+ # @param wildcard [String] wildcard to be used in the URL
33
+ # @param select_options [Hash]
34
+ # @return [String] options HTML
35
+ def polymorphic_options(metadata, wildcard = 'QUERY', select_options = {})
36
+ urls = metadata[:remote_urls] || {}
37
+ options = (metadata[:polymorphic_list] || []).map do |klass|
38
+ [
39
+ klass, klass,
40
+ { data: { url: remote_url(urls[klass], klass, wildcard) } }
41
+ ]
42
+ end
43
+ options_for_select options, select_options
44
+ end
45
+
46
+ # To fetch the hints from the following keys:
47
+ # - hints.#\{ type \}_html
48
+ # - hints.#\{ type \}
49
+ # @param metadata [Hash]
50
+ # @return [String, nil] HTML
51
+ def hint_of(metadata)
52
+ type = metadata[:type]
53
+ hint = metadata[:hint]
54
+ # @see http://guides.rubyonrails.org/i18n.html#using-safe-html-translations
55
+ hint ||= type && t("hints.#{type}_html", default: '').presence
56
+ hint ||= type && t("hints.#{type}", default: '').presence
57
+ return unless hint
58
+
59
+ content_tag :p, hint, class: 'help-block'
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Helper methods for index action
5
+ module IndexHelper
6
+ # @deprecated This method is no longer in use. It will be removed from 5.3.*
7
+ def index_params(parameters = params)
8
+ Utils.deprecate 'deprecation.index_params', caller: caller
9
+ parameters
10
+ end
11
+
12
+ # @deprecated Use {Wallaby::Paginatable#current_paginator} instead. It will be removed from 5.3.*
13
+ def paginator_of(_model_class, _collection, _params)
14
+ Utils.deprecate 'deprecation.paginator_of', caller: caller
15
+ current_paginator
16
+ end
17
+
18
+ # Just a label
19
+ # @return [String]
20
+ def all_label
21
+ t 'filters.all'
22
+ end
23
+
24
+ # If `:fields` parameter is given, only display fields that is in `index_field_names`
25
+ # Otherwise, `index_field_names`
26
+ # @param decorated_collection [Array<Wallaby::ResourceDecorator>]
27
+ # @param fields_from_params [Array<String>]
28
+ # @return [Array<String>] a list of field names for json builder
29
+ def json_fields_of(decorated_collection, fields_from_params = params[:fields])
30
+ return [] if decorated_collection.blank?
31
+
32
+ decorated = decorated_collection.first
33
+ index_field_names = decorated.index_field_names.map(&:to_s)
34
+ fields = (fields_from_params.presence || index_field_names).split(/\s*,\s*/).flatten
35
+ fields & index_field_names
36
+ end
37
+
38
+ # @param filter_name [String, Symbol]
39
+ # @param filters [Hash]
40
+ # @return [String] filter label for the given field name
41
+ def filter_label(filter_name, filters)
42
+ # TODO: use locale for filter_name label
43
+ filters[filter_name].try(:[], :label) || filter_name.to_s.humanize
44
+ end
45
+
46
+ # @param filter_name [String, Symbol]
47
+ # @param filters [Hash]
48
+ # @return [String] filter name
49
+ # @see Wallaby::FilterUtils.filter_name_by
50
+ def filter_name_by(filter_name, filters)
51
+ FilterUtils.filter_name_by filter_name, filters
52
+ end
53
+
54
+ # Link for a given model class and filter name
55
+ # @param model_class [Class]
56
+ # @param filter_name [String, Symbol]
57
+ # @param filters [Hash]
58
+ # @param url_params [Hash, ActionController::Parameters]
59
+ # @return [String] HTML anchor link
60
+ def filter_link(model_class, filter_name, filters: {}, url_params: {})
61
+ is_all = filter_name == :all
62
+ config = filters[filter_name] || {}
63
+ label = is_all ? all_label : filter_label(filter_name, filters)
64
+ url_params[:filter] = config[:default] ? nil : filter_name
65
+ index_link(model_class, options: { url: url_for(url_params.merge(with_query: true)) }) { label }
66
+ end
67
+
68
+ # @param model_class [Class]
69
+ # @param url_params [Hash, ActionController::Parameters] extra URL params
70
+ # @return [String] Export link for the given model_class.
71
+ def export_link(model_class, url_params: {})
72
+ index_link(model_class, url_params: { format: 'csv', page: nil, per: nil, with_query: true }.merge(url_params)) do
73
+ t 'links.export', ext: 'CSV'
74
+ end
75
+ end
76
+
77
+ # @return [Wallaby::Sorting::LinkBuilder]
78
+ # @see Wallaby::Sorting::LinkBuilder
79
+ def sort_link_builder
80
+ @sort_link_builder ||=
81
+ Sorting::LinkBuilder.new current_model_decorator, params.slice(:sort).permit!, self, sorting.strategy
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ # Links helper
5
+ module LinksHelper
6
+ # Return link to index page for a given model class
7
+ # @param model_class [Class]
8
+ # @param options [Hash]
9
+ # @option options [String] :url url/path for the link
10
+ # @param url_params [ActionController::Parameters, Hash]
11
+ # @param html_options [Hash] (see
12
+ # {https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
13
+ # ActionView::Helpers::UrlHelper#link_to})
14
+ # @yield block to return the link label
15
+ # @return [String] anchor link to index page for a given model class
16
+ # @return [nil] if user's not authorized
17
+ def index_link(model_class, options: {}, url_params: {}, html_options: {}, &block)
18
+ return if unauthorized? :index, model_class
19
+
20
+ html_options, block = LinkOptionsNormalizer.normalize(
21
+ html_options, block,
22
+ block: -> { to_model_label model_class }
23
+ )
24
+
25
+ url_params = request.query_parameters.merge(url_params) if url_params.delete(:with_query)
26
+ url = options[:url] || index_path(model_class, url_params: url_params)
27
+ link_to url, html_options, &block
28
+ end
29
+
30
+ # Return link to create page for a given model class
31
+ # @param model_class [Class]
32
+ # @param options [Hash]
33
+ # @option options [String] :url url/path for the link
34
+ # @param url_params [ActionController::Parameters, Hash]
35
+ # @param html_options [Hash] (see
36
+ # {https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
37
+ # ActionView::Helpers::UrlHelper#link_to})
38
+ # @yield block to return the link label
39
+ # @return [String, nil] anchor element
40
+ # @return [nil] if user's not authorized
41
+ def new_link(model_class, options: {}, url_params: {}, html_options: {}, &block)
42
+ return if unauthorized? :new, model_class
43
+
44
+ html_options, block = LinkOptionsNormalizer.normalize(
45
+ html_options, block,
46
+ class: 'resource__create',
47
+ block: -> { t 'links.new', model: to_model_label(model_class) }
48
+ )
49
+
50
+ url = options[:url] || new_path(model_class, url_params: url_params)
51
+ link_to url, html_options, &block
52
+ end
53
+
54
+ # Return link to show page for a given model class.
55
+ # @param resource [Object, Wallaby::ResourceDecorator] model class
56
+ # @param options [Hash]
57
+ # @option options [String] :url url/path for the link
58
+ # @option options [Boolean] :readonly readonly and therefore output the label
59
+ # @option options [Boolean] :is_resource to tell {#show_path} if it is a resource
60
+ # @param url_params [ActionController::Parameters, Hash]
61
+ # @param html_options [Hash] (see
62
+ # {https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
63
+ # ActionView::Helpers::UrlHelper#link_to})
64
+ # @yield block to return the link label
65
+ # @return [String] anchor element / text
66
+ # @return [nil] if user's not authorized
67
+ def show_link(resource, options: {}, url_params: {}, html_options: {}, &block)
68
+ # NOTE: to_s is a must
69
+ # if a block is returning integer (e.g. `{ 1 }`)
70
+ # `link_to` will render blank text note inside hyper link
71
+ html_options, block = LinkOptionsNormalizer.normalize(
72
+ html_options, block,
73
+ block: -> { decorate(resource).to_label.to_s }
74
+ )
75
+
76
+ default = options[:readonly] && block.call || nil
77
+ return default if unauthorized? :show, extract(resource)
78
+
79
+ url = options[:url] || show_path(resource, is_resource: options[:is_resource], url_params: url_params)
80
+ link_to url, html_options, &block
81
+ end
82
+
83
+ # Return link to edit page for a given model class.
84
+ # @param resource [Object, Wallaby::ResourceDecorator] model class
85
+ # @param options [Hash]
86
+ # @option options [String] :url url/path for the link
87
+ # @option options [Boolean] :readonly readonly and therefore output the label
88
+ # @option options [Boolean] :is_resource to tell {#edit_path} if it is a resource
89
+ # @param url_params [ActionController::Parameters, Hash]
90
+ # @param html_options [Hash] (see
91
+ # {https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
92
+ # ActionView::Helpers::UrlHelper#link_to})
93
+ # @yield block to return the link label
94
+ # @return [String] anchor element / text
95
+ # @return [nil] if user's not authorized
96
+ def edit_link(resource, options: {}, url_params: {}, html_options: {}, &block)
97
+ html_options, block = LinkOptionsNormalizer.normalize(
98
+ html_options, block,
99
+ class: 'resource__update',
100
+ block: -> { "#{t 'links.edit'} #{decorate(resource).to_label}" }
101
+ )
102
+
103
+ default = options[:readonly] && block.call || nil
104
+ return default if unauthorized? :edit, extract(resource)
105
+
106
+ url = options[:url] || edit_path(resource, is_resource: options[:is_resource], url_params: url_params)
107
+ link_to url, html_options, &block
108
+ end
109
+
110
+ # Return link to delete action for a given model class.
111
+ # @param resource [Object, Wallaby::ResourceDecorator] model class
112
+ # @param options [Hash]
113
+ # @option options [String] :url url/path for the link
114
+ # @option options [Boolean] :is_resource to tell {#edit_path} if it is a resource
115
+ # @param url_params [ActionController::Parameters, Hash]
116
+ # @param html_options [Hash] (see
117
+ # {https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
118
+ # ActionView::Helpers::UrlHelper#link_to})
119
+ # @yield block to return the link label
120
+ # @return [String, nil] anchor element
121
+ # @return [nil] if user's not authorized
122
+ def delete_link(resource, options: {}, url_params: {}, html_options: {}, &block)
123
+ return if unauthorized? :destroy, extract(resource)
124
+
125
+ html_options, block = LinkOptionsNormalizer.normalize(
126
+ html_options, block, class: 'resource__destroy', block: -> { t 'links.delete' }
127
+ )
128
+
129
+ html_options[:method] ||= :delete
130
+ (html_options[:data] ||= {})[:confirm] ||= t 'links.confirm.delete'
131
+
132
+ url = options[:url] || show_path(resource, is_resource: options[:is_resource], url_params: url_params)
133
+ link_to url, html_options, &block
134
+ end
135
+
136
+ # Return link to cancel action
137
+ # @param html_options [Hash] (see
138
+ # {https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
139
+ # ActionView::Helpers::UrlHelper#link_to})
140
+ # @yield block to return the link label
141
+ # @return [String] anchor link of cancel action
142
+ def cancel_link(html_options: {}, &block)
143
+ block ||= -> { t 'links.cancel' }
144
+ link_to 'javascript:history.back()', html_options, &block
145
+ end
146
+
147
+ # @param model_class [Class]
148
+ # @param url_params [Hash]
149
+ # @return [String] index page path
150
+ def index_path(model_class, url_params: {})
151
+ hash = ParamsUtils.presence(
152
+ { action: :index },
153
+ default_path_params(resources: to_resources_name(model_class)),
154
+ url_params.to_h
155
+ )
156
+ current_engine.try(:resources_path, hash) || url_for(hash)
157
+ end
158
+
159
+ # @param model_class [Class]
160
+ # @param url_params [Hash]
161
+ # @return [String] new page path
162
+ def new_path(model_class, url_params: {})
163
+ hash = ParamsUtils.presence(
164
+ { action: :new },
165
+ default_path_params(resources: to_resources_name(model_class)),
166
+ url_params.to_h
167
+ )
168
+ current_engine.try(:new_resource_path, hash) || url_for(hash)
169
+ end
170
+
171
+ # @param resource [Object]
172
+ # @param is_resource [Boolean]
173
+ # @param url_params [Hash]
174
+ # @return [String] show page path
175
+ def show_path(resource, is_resource: false, url_params: {})
176
+ decorated = decorate resource
177
+ return unless is_resource || decorated.primary_key_value
178
+
179
+ hash = ParamsUtils.presence(
180
+ { action: :show, id: decorated.primary_key_value },
181
+ default_path_params(resources: decorated.resources_name),
182
+ url_params.to_h
183
+ )
184
+
185
+ current_engine.try(:resource_path, hash) || url_for(hash)
186
+ end
187
+
188
+ # @param resource [Object]
189
+ # @param is_resource [Boolean]
190
+ # @param url_params [Hash]
191
+ # @return [String] edit page path
192
+ def edit_path(resource, is_resource: false, url_params: {})
193
+ decorated = decorate resource
194
+ return unless is_resource || decorated.primary_key_value
195
+
196
+ hash = ParamsUtils.presence(
197
+ { action: :edit, id: decorated.primary_key_value },
198
+ default_path_params(resources: decorated.resources_name),
199
+ url_params.to_h
200
+ )
201
+
202
+ current_engine.try(:edit_resource_path, hash) || url_for(hash)
203
+ end
204
+
205
+ # @return [Hash] default path params
206
+ def default_path_params(resources: nil)
207
+ { script_name: request.env[SCRIPT_NAME] }.tap do |default|
208
+ default[:resources] = resources if current_engine || resources
209
+ default[:only_path] = true unless default.key?(:only_path)
210
+ end
211
+ end
212
+ end
213
+ end