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.
- 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,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Type renderer
|
|
5
|
+
class TypeRenderer
|
|
6
|
+
class << self
|
|
7
|
+
# Render partial
|
|
8
|
+
# @param view [ActionView]
|
|
9
|
+
# @param options [Hash]
|
|
10
|
+
# @param locals [Hash]
|
|
11
|
+
# @return [String] HTML
|
|
12
|
+
def render(view, options = {}, locals = {}, &block)
|
|
13
|
+
locals[:object] ||= locals[:form].try :object
|
|
14
|
+
check locals
|
|
15
|
+
complete locals, view.params[:action]
|
|
16
|
+
view.render options, locals, &block
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# @param locals [Hash]
|
|
22
|
+
# @raise [ArgumentError] if form is set but blank
|
|
23
|
+
# @raise [ArgumentError] if field_name is not provided
|
|
24
|
+
# @raise [ArgumentError] if object is not decorated
|
|
25
|
+
def check(locals)
|
|
26
|
+
raise ArgumentError, I18n.t('errors.required', subject: 'form') if locals.key?(:form) && locals[:form].blank?
|
|
27
|
+
raise ArgumentError, I18n.t('errors.required', subject: 'field_name') if locals[:field_name].blank?
|
|
28
|
+
raise ArgumentError, 'Object is not decorated.' unless locals[:object].is_a? ResourceDecorator
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param locals [Hash]
|
|
32
|
+
# @param action [String]
|
|
33
|
+
def complete(locals, action)
|
|
34
|
+
action_name = CellUtils.to_action_prefix action
|
|
35
|
+
locals[:metadata] = locals[:object].public_send :"#{action_name}_metadata_of", locals[:field_name]
|
|
36
|
+
locals[:value] = locals[:object].public_send locals[:field_name]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param options [String]
|
|
40
|
+
# @param view [ActionView]
|
|
41
|
+
# @return [String] partial path string
|
|
42
|
+
# @return [String] blank string
|
|
43
|
+
def find_partial(options, view)
|
|
44
|
+
formats = [view.request.format.to_sym]
|
|
45
|
+
lookup = view.lookup_context
|
|
46
|
+
lookup.find_template options, lookup.prefixes, true, [], formats: formats
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionDispatch
|
|
4
|
+
module Routing
|
|
5
|
+
# Re-open `ActionDispatch::Routing::Mapper` to add route helpers for Wallaby.
|
|
6
|
+
class Mapper
|
|
7
|
+
# Generate **resourceful** routes that works for Wallaby.
|
|
8
|
+
# @example To generate resourceful routes that works for Wallaby:
|
|
9
|
+
# wresources :postcodes
|
|
10
|
+
# # => same effect as
|
|
11
|
+
# resources(
|
|
12
|
+
# :postcodes,
|
|
13
|
+
# path: ':resources',
|
|
14
|
+
# defaults: { resources: :postcodes },
|
|
15
|
+
# constraints: { resources: :postcodes }
|
|
16
|
+
# )
|
|
17
|
+
# @param resource_names [Array<String, Symbol>]
|
|
18
|
+
# @see https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources
|
|
19
|
+
# ActionDispatch::Routing::Mapper::Resources#resources
|
|
20
|
+
def wresources(*resource_names, &block)
|
|
21
|
+
options = resource_names.extract_options!.dup
|
|
22
|
+
resource_names.each do |resource_name|
|
|
23
|
+
new_options = wallaby_resources_options_for resource_name, options
|
|
24
|
+
resources resource_name, new_options, &block
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Generate **resourceful** routes that works for Wallaby.
|
|
29
|
+
# @example To generate resourceful routes that works for Wallaby:
|
|
30
|
+
# wresource :profile
|
|
31
|
+
# # => same effect as
|
|
32
|
+
# resource(
|
|
33
|
+
# :profile,
|
|
34
|
+
# path: ':resource',
|
|
35
|
+
# defaults: { resource: :profile, resources: :profiles },
|
|
36
|
+
# constraints: { resource: :profile, resources: :profiles }
|
|
37
|
+
# )
|
|
38
|
+
# @param resource_names [Array<String, Symbol>]
|
|
39
|
+
# @see https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resource
|
|
40
|
+
# ActionDispatch::Routing::Mapper::Resources#resource
|
|
41
|
+
def wresource(*resource_names, &block)
|
|
42
|
+
options = resource_names.extract_options!.dup
|
|
43
|
+
resource_names.each do |resource_name|
|
|
44
|
+
new_options = wallaby_resource_options_for resource_name, options
|
|
45
|
+
resource resource_name, new_options, &block
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
protected
|
|
50
|
+
|
|
51
|
+
# Fill in the **resources** options required by Wallaby
|
|
52
|
+
# @param resources_name [String, Symbol]
|
|
53
|
+
# @param options [Hash]
|
|
54
|
+
def wallaby_resources_options_for(resources_name, options)
|
|
55
|
+
{ path: ':resources' }.merge!(options).tap do |new_options|
|
|
56
|
+
%i(defaults constraints).each do |key|
|
|
57
|
+
new_options[key] = { resources: resources_name }.merge!(new_options[key] || {})
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Fill in the **resource** options required by Wallaby
|
|
63
|
+
# @param resource_name [String, Symbol]
|
|
64
|
+
# @param options [Hash]
|
|
65
|
+
def wallaby_resource_options_for(resource_name, options)
|
|
66
|
+
plural_resources = Wallaby::ModelUtils.to_resources_name resource_name
|
|
67
|
+
{ path: ':resource' }.merge!(options).tap do |new_options|
|
|
68
|
+
%i(defaults constraints).each do |key|
|
|
69
|
+
new_options[key] = { resource: resource_name, resources: plural_resources }.merge!(new_options[key] || {})
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# To present classes in tree structure.
|
|
5
|
+
class Node
|
|
6
|
+
# @!attribute [r] klass
|
|
7
|
+
# Represent the current class
|
|
8
|
+
attr_reader :klass
|
|
9
|
+
# @!attribute parent
|
|
10
|
+
# Represent the parent class of current class
|
|
11
|
+
attr_accessor :parent
|
|
12
|
+
|
|
13
|
+
delegate :name, to: :klass
|
|
14
|
+
|
|
15
|
+
# @param klass [Class]
|
|
16
|
+
def initialize(klass)
|
|
17
|
+
@klass = klass
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @return [Array<Class>] a list of children classes
|
|
21
|
+
def children
|
|
22
|
+
@children ||= []
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Cell utils
|
|
5
|
+
module CellUtils
|
|
6
|
+
class << self
|
|
7
|
+
# Render a cell and produce output
|
|
8
|
+
# @param context [ActionView::Context]
|
|
9
|
+
# @param file_name [String]
|
|
10
|
+
# @param locals [Hash]
|
|
11
|
+
# @return [String] output
|
|
12
|
+
def render(context, file_name, locals = {}, &block)
|
|
13
|
+
snake_class = file_name[%r{(?<=app/).+(?=\.rb)}].split(SLASH, 2).last
|
|
14
|
+
cell_class = snake_class.camelize.constantize
|
|
15
|
+
Rails.logger.info " Rendered [cell] #{file_name}"
|
|
16
|
+
cell_class.new(context, locals).render_complete(&block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Check if a partial is a cell or not
|
|
20
|
+
# @param partial_path [String]
|
|
21
|
+
# @return [true] if partial is a `rb` file
|
|
22
|
+
# @return [false] otherwise
|
|
23
|
+
def find_cell(*partials)
|
|
24
|
+
partials.find { |partial| partial.end_with? '.rb' }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param action_name [String, Symbol]
|
|
28
|
+
# @return [String, Symbol] action prefix
|
|
29
|
+
def to_action_prefix(action_name)
|
|
30
|
+
FORM_ACTIONS[action_name] || action_name
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Field utils
|
|
5
|
+
module FieldUtils
|
|
6
|
+
class << self
|
|
7
|
+
# Find the first field that meets the first condition.
|
|
8
|
+
# @example to find the possible text field
|
|
9
|
+
# Wallaby::FieldUtils.first_field_by({ name: /name|title/ }, { type: 'string' }, fields)
|
|
10
|
+
# # => if any field name that has `name` or `title`, return this field
|
|
11
|
+
# # => otherwise, find the first field that has type `string`
|
|
12
|
+
# @param conditions [Array<Hash>]
|
|
13
|
+
# @param fields [Hash] field metadata
|
|
14
|
+
# @return [String, Symbol] field name
|
|
15
|
+
def first_field_by(*conditions, fields)
|
|
16
|
+
return if [conditions, fields].any?(&:blank?)
|
|
17
|
+
|
|
18
|
+
conditions.each do |condition|
|
|
19
|
+
fields.each do |field_name, metadata|
|
|
20
|
+
return field_name if meet? field_name, metadata.with_indifferent_access, condition
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
# @param field_name [String]
|
|
29
|
+
# @param metadata [Hash]
|
|
30
|
+
# @param condition [Hash]
|
|
31
|
+
# @return [true] if field's metadata meets the condition
|
|
32
|
+
# @return [true] otherwise
|
|
33
|
+
def meet?(field_name, metadata, condition)
|
|
34
|
+
condition.all? do |key, requirement|
|
|
35
|
+
operator = requirement.is_a?(::Regexp) ? '=~' : '=='
|
|
36
|
+
value = metadata[key]
|
|
37
|
+
value ||= field_name.to_s if key.to_sym == :name
|
|
38
|
+
ModuleUtils.try_to value, operator, requirement
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Filter utils
|
|
5
|
+
module FilterUtils
|
|
6
|
+
# Find filter name in the following precedences from high to low:
|
|
7
|
+
#
|
|
8
|
+
# - `filter_name` argument
|
|
9
|
+
# - filters that has been marked as default
|
|
10
|
+
# - `:all`
|
|
11
|
+
# @param filter_name [String, Symbol] filter name
|
|
12
|
+
# @param filters [Hash] filter metadata
|
|
13
|
+
# @return [String, Symbol]
|
|
14
|
+
def self.filter_name_by(filter_name, filters)
|
|
15
|
+
filter = filter_name # from param
|
|
16
|
+
filter ||= filters.find { |_k, v| v[:default] }.try(:first) # from default value
|
|
17
|
+
filter || :all # last resort
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Utils for model
|
|
5
|
+
module ModelUtils
|
|
6
|
+
class << self
|
|
7
|
+
# Convert model class (e.g. `Namespace::Product`) into resources name (e.g. `namespace::products`)
|
|
8
|
+
# @param model_class [Class, String] model class
|
|
9
|
+
# @return [String] resources name
|
|
10
|
+
def to_resources_name(model_class)
|
|
11
|
+
return EMPTY_STRING if model_class.blank?
|
|
12
|
+
|
|
13
|
+
model_class.to_s.underscore.gsub(SLASH, COLONS).pluralize
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Produce model label (e.g. `Namespace / Product`) for model class (e.g. `Namespace::Product`)
|
|
17
|
+
# @param model_class [Class, String] model class
|
|
18
|
+
# @return [String] model label
|
|
19
|
+
def to_model_label(model_class)
|
|
20
|
+
# TODO: change to use i18n translation
|
|
21
|
+
return EMPTY_STRING if model_class.blank?
|
|
22
|
+
|
|
23
|
+
model_class_name = to_model_name model_class
|
|
24
|
+
model_class_name.titleize.gsub(SLASH, SPACE + SLASH + SPACE)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Convert resources name (e.g. `namespace::products`) into model class (e.g. `Namespace::Product`)
|
|
28
|
+
# @param resources_name [String] resources name
|
|
29
|
+
# @return [Class] model class
|
|
30
|
+
# @return [nil] when not found
|
|
31
|
+
def to_model_class(resources_name)
|
|
32
|
+
return if resources_name.blank?
|
|
33
|
+
|
|
34
|
+
class_name = to_model_name resources_name
|
|
35
|
+
class_name.constantize
|
|
36
|
+
rescue NameError
|
|
37
|
+
Rails.logger.warn I18n.t('errors.not_found.model', model: class_name)
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Convert resources name (e.g. `namespace::products`) into model name (e.g. `Namespace::Product`)
|
|
42
|
+
# @param resources_name [String] resources name
|
|
43
|
+
# @return [String] model name
|
|
44
|
+
def to_model_name(resources_name)
|
|
45
|
+
return EMPTY_STRING if resources_name.blank?
|
|
46
|
+
|
|
47
|
+
resources_name.to_s.singularize.gsub(COLONS, SLASH).camelize
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Utils for module and class
|
|
5
|
+
module ModuleUtils
|
|
6
|
+
class << self
|
|
7
|
+
# A helper method to check if subject responds to given method and to return the result if so
|
|
8
|
+
# @param subject [Object]
|
|
9
|
+
# @param method_id [String, Symbol]
|
|
10
|
+
# @param args [Array] a list of arguments
|
|
11
|
+
# @return [Object] result from executing given method on subject
|
|
12
|
+
# @return [nil] if subject doesn't respond to given method
|
|
13
|
+
def try_to(subject, method_id, *args, &block)
|
|
14
|
+
return if method_id.blank?
|
|
15
|
+
|
|
16
|
+
subject.respond_to?(method_id) && subject.public_send(method_id, *args, &block) || nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Check whether a class is anonymous or not
|
|
20
|
+
# @param klass [Class]
|
|
21
|
+
# @return [true] if a class is anonymous
|
|
22
|
+
# @return [false] otherwise
|
|
23
|
+
def anonymous_class?(klass)
|
|
24
|
+
klass.name.blank? || klass.to_s.start_with?('#<Class')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Check if a child class inherits from parent class
|
|
28
|
+
# @param child [Class] child class
|
|
29
|
+
# @param parent [Class] parent class
|
|
30
|
+
# @raise [ArgumentError] if given class is not a child of the other class
|
|
31
|
+
def inheritance_check(child, parent)
|
|
32
|
+
return unless child && parent
|
|
33
|
+
return if child < parent
|
|
34
|
+
|
|
35
|
+
raise ::ArgumentError, I18n.t('errors.invalid.inheritance', klass: child, parent: parent)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# If block is given, run the block. Otherwise, return subject
|
|
39
|
+
# @param subject [Object]
|
|
40
|
+
# @yield [subject]
|
|
41
|
+
def yield_for(subject)
|
|
42
|
+
block_given? ? yield(subject) : subject
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Hash utils
|
|
5
|
+
module ParamsUtils
|
|
6
|
+
class << self
|
|
7
|
+
# @param params [Array<Hash>]
|
|
8
|
+
# @return [Hash] combined hash that removes empty values
|
|
9
|
+
def presence(*params)
|
|
10
|
+
params.reduce({}, :merge).delete_if { |_, v| v.nil? || v == '' }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Preload utils
|
|
5
|
+
module PreloadUtils
|
|
6
|
+
class << self
|
|
7
|
+
# Preload all files under app folder
|
|
8
|
+
def require_all
|
|
9
|
+
eager_load_paths.map(&method(:require_one))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Require files under a load path
|
|
13
|
+
# @param load_path [String, Pathname]
|
|
14
|
+
# @see https://api.rubyonrails.org/classes/Rails/Engine.html#method-i-eager_load-21 Rails::Engine#eager_load!
|
|
15
|
+
def require_one(load_path)
|
|
16
|
+
Dir.glob("#{load_path}/**/*.rb").sort.each(&method(:load_class_for))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
protected
|
|
20
|
+
|
|
21
|
+
# @return [Array<String, Pathname>] a list of sorted eager load paths which lists `app/models`
|
|
22
|
+
# at highest precedence
|
|
23
|
+
def eager_load_paths
|
|
24
|
+
Rails.configuration.eager_load_paths.sort_by do |path|
|
|
25
|
+
- path.index(%r{/models$}).to_i
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# `constantize` is used to make Rails to handle all sort of load errors
|
|
30
|
+
#
|
|
31
|
+
# NOTE: don't try to use `ActiveSupport::Dependencies::Loadable.require_dependency`.
|
|
32
|
+
# As `require_dependency` does not take care all errors raised when class/module is loaded.
|
|
33
|
+
# @param file_path [Pathname, String]
|
|
34
|
+
def load_class_for(file_path)
|
|
35
|
+
module_name = file_path[%r{app/[^/]+/(.+)\.rb}, 1].gsub('/concerns/', '/')
|
|
36
|
+
class_name = module_name.camelize
|
|
37
|
+
class_name.constantize unless Module.const_defined? class_name
|
|
38
|
+
rescue NameError, LoadError => e
|
|
39
|
+
Rails.logger.debug " [WALLABY] Preload warning: #{e.message} from #{file_path}"
|
|
40
|
+
Rails.logger.debug e.backtrace.slice(0, 5)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Wallaby
|
|
4
|
+
# Utils for test
|
|
5
|
+
module TestUtils
|
|
6
|
+
class << self
|
|
7
|
+
# @param context
|
|
8
|
+
# @param controller_class [Class]
|
|
9
|
+
def around_crud(context, controller_class = nil)
|
|
10
|
+
context.before do
|
|
11
|
+
controller_class ||= described_class
|
|
12
|
+
controller_path = controller_class.controller_path
|
|
13
|
+
Wallaby::TestUtils.draw(routes, controller_path)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context.after { Rails.application.reload_routes! }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @param routes
|
|
20
|
+
# @param controller_path [String]
|
|
21
|
+
def draw(routes, controller_path)
|
|
22
|
+
routes.draw do
|
|
23
|
+
get ':resources', to: "#{controller_path}#index", as: :resources
|
|
24
|
+
get ':resources/:id', to: "#{controller_path}#show", as: :resource
|
|
25
|
+
get ':resources/new', to: "#{controller_path}#new"
|
|
26
|
+
get ':resources/:id/edit', to: "#{controller_path}#edit"
|
|
27
|
+
post ':resources', to: "#{controller_path}#create"
|
|
28
|
+
patch ':resources/:id', to: "#{controller_path}#update"
|
|
29
|
+
delete ':resources/:id', to: "#{controller_path}#destroy"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|