wallaby-core 0.1.2 → 0.2.3

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -13
  3. data/app/controllers/wallaby/resources_controller.rb +15 -374
  4. data/app/security/ability.rb +1 -1
  5. data/config/locales/wallaby.en.yml +92 -128
  6. data/config/locales/wallaby_class.en.yml +2 -23
  7. data/lib/adaptors/wallaby/custom/default_provider.rb +1 -1
  8. data/lib/adaptors/wallaby/custom/model_decorator.rb +8 -7
  9. data/lib/adaptors/wallaby/custom/model_finder.rb +3 -2
  10. data/lib/adaptors/wallaby/custom/model_pagination_provider.rb +1 -1
  11. data/lib/adaptors/wallaby/custom/model_service_provider.rb +1 -40
  12. data/lib/authorizers/wallaby/cancancan_authorization_provider.rb +30 -24
  13. data/lib/authorizers/wallaby/default_authorization_provider.rb +6 -13
  14. data/lib/authorizers/wallaby/model_authorizer.rb +43 -67
  15. data/lib/authorizers/wallaby/pundit_authorization_provider.rb +21 -30
  16. data/lib/concerns/wallaby/application_concern.rb +110 -0
  17. data/lib/concerns/wallaby/authentication_concern.rb +162 -0
  18. data/lib/concerns/wallaby/authorizable.rb +8 -14
  19. data/lib/concerns/wallaby/baseable.rb +91 -10
  20. data/lib/concerns/wallaby/decoratable.rb +3 -3
  21. data/lib/concerns/wallaby/engineable.rb +1 -1
  22. data/lib/concerns/wallaby/fieldable.rb +4 -4
  23. data/lib/concerns/wallaby/paginatable.rb +3 -3
  24. data/lib/concerns/wallaby/prefixable.rb +21 -0
  25. data/lib/concerns/wallaby/resourcable.rb +4 -35
  26. data/lib/concerns/wallaby/resources_concern.rb +434 -0
  27. data/lib/concerns/wallaby/servicable.rb +4 -10
  28. data/lib/decorators/wallaby/resource_decorator.rb +53 -80
  29. data/lib/errors/wallaby/{cell_handling.rb → class_not_found.rb} +1 -1
  30. data/lib/errors/wallaby/model_not_found.rb +3 -1
  31. data/lib/errors/wallaby/resource_not_found.rb +1 -1
  32. data/lib/helpers/wallaby/application_helper.rb +6 -9
  33. data/lib/helpers/wallaby/form_helper.rb +2 -9
  34. data/lib/helpers/wallaby/index_helper.rb +2 -14
  35. data/lib/helpers/wallaby/links_helper.rb +5 -5
  36. data/lib/helpers/wallaby/resources_helper.rb +3 -7
  37. data/lib/helpers/wallaby/secure_helper.rb +3 -3
  38. data/lib/helpers/wallaby/styling_helper.rb +17 -3
  39. data/lib/interfaces/wallaby/mode.rb +5 -5
  40. data/lib/interfaces/wallaby/model_authorization_provider.rb +15 -13
  41. data/lib/interfaces/wallaby/model_decorator.rb +15 -3
  42. data/lib/paginators/wallaby/model_paginator.rb +14 -45
  43. data/lib/parsers/wallaby/parser.rb +49 -14
  44. data/lib/routes/wallaby/resources_router.rb +1 -1
  45. data/lib/servicers/wallaby/model_servicer.rb +32 -62
  46. data/lib/services/wallaby/map/mode_mapper.rb +14 -14
  47. data/lib/services/wallaby/map/model_class_collector.rb +2 -2
  48. data/lib/services/wallaby/map/model_class_mapper.rb +7 -26
  49. data/lib/services/wallaby/prefixes_builder.rb +15 -49
  50. data/lib/services/wallaby/type_renderer.rb +3 -13
  51. data/lib/utils/wallaby/locale.rb +53 -0
  52. data/lib/utils/wallaby/model_utils.rb +4 -3
  53. data/lib/utils/wallaby/module_utils.rb +1 -1
  54. data/lib/utils/wallaby/utils.rb +10 -15
  55. data/lib/wallaby/class_array.rb +75 -0
  56. data/lib/wallaby/class_hash.rb +94 -0
  57. data/lib/wallaby/classifier.rb +29 -0
  58. data/lib/wallaby/configuration/mapping.rb +33 -27
  59. data/lib/wallaby/configuration/metadata.rb +1 -1
  60. data/lib/wallaby/configuration/models.rb +5 -9
  61. data/lib/wallaby/configuration/security.rb +6 -3
  62. data/lib/wallaby/configuration/sorting.rb +1 -1
  63. data/lib/wallaby/configuration.rb +31 -2
  64. data/lib/wallaby/constants.rb +10 -8
  65. data/lib/wallaby/core/version.rb +1 -1
  66. data/lib/wallaby/core.rb +21 -16
  67. data/lib/wallaby/engine.rb +9 -20
  68. data/lib/wallaby/logger.rb +35 -0
  69. data/lib/wallaby/map.rb +20 -17
  70. data/lib/wallaby/preloader.rb +77 -0
  71. metadata +48 -22
  72. data/app/controllers/wallaby/application_controller.rb +0 -84
  73. data/app/controllers/wallaby/secure_controller.rb +0 -81
  74. data/lib/concerns/wallaby/rails_overridden_methods.rb +0 -42
  75. data/lib/concerns/wallaby/themeable.rb +0 -40
  76. data/lib/paginators/wallaby/resource_paginator.rb +0 -12
  77. data/lib/renderers/wallaby/cell.rb +0 -137
  78. data/lib/renderers/wallaby/cell_resolver.rb +0 -89
  79. data/lib/renderers/wallaby/custom_lookup_context.rb +0 -64
  80. data/lib/renderers/wallaby/custom_partial_renderer.rb +0 -33
  81. data/lib/renderers/wallaby/custom_renderer.rb +0 -16
  82. data/lib/utils/wallaby/cell_utils.rb +0 -34
  83. data/lib/utils/wallaby/preload_utils.rb +0 -44
@@ -12,15 +12,6 @@ module Wallaby
12
12
  # @see Wallaby::ModuleUtils.try_to
13
13
  delegate :try_to, to: ModuleUtils
14
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
15
  # Override origin method to handle URL for Wallaby engine.
25
16
  #
26
17
  # As Wallaby's routes are declared in a
@@ -75,5 +66,11 @@ module Wallaby
75
66
  options = default_options.merge!(sources.extract_options!.stringify_keys)
76
67
  super(*sources, options)
77
68
  end
69
+
70
+ # @param key
71
+ # @param options [Hash]
72
+ def wt(key, options = {})
73
+ Locale.t key, { translator: method(:t) }.merge(options)
74
+ end
78
75
  end
79
76
  end
@@ -3,12 +3,6 @@
3
3
  module Wallaby
4
4
  # Form helper
5
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
6
  # To generate remote URL for auto select plugin.
13
7
  # @see https://github.com/reinteractive/wallaby/blob/master/app/assets/javascripts/wallaby/auto_select.js
14
8
  # auto_select.js
@@ -52,9 +46,8 @@ module Wallaby
52
46
  type = metadata[:type]
53
47
  hint = metadata[:hint]
54
48
  # @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
49
+ hint ||= type && wt("hints.#{type}_html", default: [:"hints.#{type}", ''])
50
+ return if hint.blank?
58
51
 
59
52
  content_tag :p, hint, class: 'help-block'
60
53
  end
@@ -3,22 +3,10 @@
3
3
  module Wallaby
4
4
  # Helper methods for index action
5
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
6
  # Just a label
19
7
  # @return [String]
20
8
  def all_label
21
- t 'filters.all'
9
+ wt 'filters.all'
22
10
  end
23
11
 
24
12
  # If `:fields` parameter is given, only display fields that is in `index_field_names`
@@ -70,7 +58,7 @@ module Wallaby
70
58
  # @return [String] Export link for the given model_class.
71
59
  def export_link(model_class, url_params: {})
72
60
  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'
61
+ wt 'links.export', ext: 'CSV'
74
62
  end
75
63
  end
76
64
 
@@ -44,7 +44,7 @@ module Wallaby
44
44
  html_options, block = LinkOptionsNormalizer.normalize(
45
45
  html_options, block,
46
46
  class: 'resource__create',
47
- block: -> { t 'links.new', model: to_model_label(model_class) }
47
+ block: -> { wt 'links.new', model: to_model_label(model_class) }
48
48
  )
49
49
 
50
50
  url = options[:url] || new_path(model_class, url_params: url_params)
@@ -97,7 +97,7 @@ module Wallaby
97
97
  html_options, block = LinkOptionsNormalizer.normalize(
98
98
  html_options, block,
99
99
  class: 'resource__update',
100
- block: -> { "#{t 'links.edit'} #{decorate(resource).to_label}" }
100
+ block: -> { "#{wt 'links.edit'} #{decorate(resource).to_label}" }
101
101
  )
102
102
 
103
103
  default = options[:readonly] && block.call || nil
@@ -123,11 +123,11 @@ module Wallaby
123
123
  return if unauthorized? :destroy, extract(resource)
124
124
 
125
125
  html_options, block = LinkOptionsNormalizer.normalize(
126
- html_options, block, class: 'resource__destroy', block: -> { t 'links.delete' }
126
+ html_options, block, class: 'resource__destroy', block: -> { wt 'links.delete' }
127
127
  )
128
128
 
129
129
  html_options[:method] ||= :delete
130
- (html_options[:data] ||= {})[:confirm] ||= t 'links.confirm.delete'
130
+ (html_options[:data] ||= {})[:confirm] ||= wt 'links.confirm.delete'
131
131
 
132
132
  url = options[:url] || show_path(resource, is_resource: options[:is_resource], url_params: url_params)
133
133
  link_to url, html_options, &block
@@ -140,7 +140,7 @@ module Wallaby
140
140
  # @yield block to return the link label
141
141
  # @return [String] anchor link of cancel action
142
142
  def cancel_link(html_options: {}, &block)
143
- block ||= -> { t 'links.cancel' }
143
+ block ||= -> { wt 'links.cancel' }
144
144
  link_to 'javascript:history.back()', html_options, &block
145
145
  end
146
146
 
@@ -3,6 +3,9 @@
3
3
  module Wallaby
4
4
  # Resources helper
5
5
  module ResourcesHelper
6
+ include ApplicationHelper
7
+ include SecureHelper
8
+
6
9
  include BaseHelper
7
10
  include FormHelper
8
11
  include IndexHelper
@@ -12,13 +15,6 @@ module Wallaby
12
15
  include Paginatable
13
16
  include Resourcable
14
17
  include Servicable
15
- include Themeable
16
-
17
- # @deprecated Use {#type_render} instead. It will be removed from 5.3.*
18
- def type_partial_render(options = {}, locals = {}, &block)
19
- Utils.deprecate 'deprecation.type_partial_render', caller: caller
20
- type_render options, locals, &block
21
- end
22
18
 
23
19
  # Render type cell/partial
24
20
  # @param partial_name [String]
@@ -9,7 +9,7 @@ module Wallaby
9
9
  # - otherwise, an user icon will be returned
10
10
  # @param user [Object]
11
11
  # @return [String] IMG or I element
12
- def user_portrait(user = current_user)
12
+ def user_portrait(user = wallaby_user)
13
13
  email_method = security.email_method || :email
14
14
  email = try_to user, email_method
15
15
  if email.present?
@@ -27,7 +27,7 @@ module Wallaby
27
27
  # @param user [Object]
28
28
  # @param app [Object]
29
29
  # @return [String] URL to log out
30
- def logout_path(user = current_user, app = main_app)
30
+ def logout_path(user = wallaby_user, app = main_app)
31
31
  path = security.logout_path
32
32
  path ||=
33
33
  if defined? ::Devise
@@ -41,7 +41,7 @@ module Wallaby
41
41
  # @see Wallaby::Configuration::Security#logout_method
42
42
  # @param user [Object]
43
43
  # @return [String, Symbol] http method to log out
44
- def logout_method(user = current_user)
44
+ def logout_method(user = wallaby_user)
45
45
  http_method = security.logout_method
46
46
  http_method ||
47
47
  if defined? ::Devise
@@ -3,6 +3,20 @@
3
3
  module Wallaby
4
4
  # Helper methods to build custom elements
5
5
  module StylingHelper
6
+ # backforward compatible with FontAwesome 4
7
+ # @see this fa migration document https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4
8
+ FONT_AWESOME_MAPPING = {
9
+ 4 => {
10
+ clock: 'clock-o',
11
+ bars: 'navicon',
12
+ calendar: 'calendar-o',
13
+ 'check-square': 'check-square-o',
14
+ square: 'square-o',
15
+ link: 'chain',
16
+ user: 'user-o'
17
+ }
18
+ }.with_indifferent_access.freeze
19
+
6
20
  # Shortcut to build up the HTML options as keyword arguments
7
21
  # @param string_or_array [String, Array<String>]
8
22
  # @param options [Hash]
@@ -54,12 +68,12 @@ module Wallaby
54
68
 
55
69
  # @return [String] grey null
56
70
  def null
57
- muted t 'labels.empty'
71
+ muted wt 'labels.empty'
58
72
  end
59
73
 
60
74
  # @return [String] grey N/A
61
75
  def na
62
- muted t 'labels.na'
76
+ muted wt 'labels.na'
63
77
  end
64
78
 
65
79
  # Grey text
@@ -74,7 +88,7 @@ module Wallaby
74
88
  def fa_map(name, major = nil)
75
89
  @map ||= begin
76
90
  major ||= Gem.loaded_specs['font-awesome-sass'].try(:version).try(:segments).try(:first)
77
- t("fa.v#{major}").with_indifferent_access
91
+ FONT_AWESOME_MAPPING[major] || {}
78
92
  end
79
93
  @map[name] || name
80
94
  end
@@ -24,14 +24,14 @@ module Wallaby
24
24
 
25
25
  # @see Wallaby::ModelPaginationProvider
26
26
  # @return [Class] pagination provider for the mode
27
- # @since 5.2.0
27
+ # @since wallaby-5.2.0
28
28
  def model_pagination_provider
29
29
  check_and_constantize __callee__
30
30
  end
31
31
 
32
32
  # @see Wallaby::ModelPaginationProvider
33
33
  # @return [Class] pagination provider for the mode
34
- # @since 5.2.0
34
+ # @since wallaby-5.2.0
35
35
  def default_authorization_provider
36
36
  check_and_constantize __callee__
37
37
  end
@@ -39,7 +39,7 @@ module Wallaby
39
39
  # Return a list of authorization providers for authorizer to detect which one to use.
40
40
  # @see Wallaby::ModelAuthorizationProvider
41
41
  # @return [ActiveSupport::HashWithIndifferentAccess<String, Class>] authorization provider hash
42
- # @since 5.2.0
42
+ # @since wallaby-5.2.0
43
43
  def model_authorization_providers(classes = ModelAuthorizationProvider.descendants)
44
44
  # NOTE: make sure default provider always comes last
45
45
  @model_authorization_providers ||=
@@ -61,10 +61,10 @@ module Wallaby
61
61
  class_name.constantize.tap do |klass|
62
62
  next if klass < parent_class
63
63
 
64
- raise InvalidError, I18n.t('wallaby.mode.inherit_required', klass: klass, parent: parent_class)
64
+ raise InvalidError, Locale.t('mode.inherit_required', klass: klass, parent: parent_class)
65
65
  end
66
66
  rescue NameError => e
67
- Rails.logger.error e
67
+ Logger.error e
68
68
  raise NotImplemented, class_name
69
69
  end
70
70
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Wallaby
4
4
  # Model Authorizer interface.
5
- # @since 5.2.0
5
+ # @since wallaby-5.2.0
6
6
  class ModelAuthorizationProvider
7
7
  class << self
8
8
  # @!attribute [w] provider_name
@@ -13,34 +13,36 @@ module Wallaby
13
13
  # @see Wallaby::ModelAuthorizer.provider_name
14
14
  # @return [String/Symbol] provider name
15
15
  def provider_name
16
- @provider_name || name.demodulize.gsub(/(Authorization)?Provider/, EMPTY_STRING).underscore
16
+ @provider_name ||= name.demodulize.gsub(/(Authorization)?Provider/, EMPTY_STRING).underscore
17
17
  end
18
18
 
19
19
  # @note Template method to check and see if current provider is in used.
20
- # @param _context [ActionController::Base]
20
+ # @param _context [ActionController::Base, ActionView::Base]
21
21
  # @raise [Wallaby::NotImplemented]
22
22
  def available?(_context)
23
23
  raise NotImplemented
24
24
  end
25
-
26
- # @note Template method to pull out the args required for contruction from context.
27
- # @param _context [ActionController::Base]
28
- # @raise [Wallaby::NotImplemented]
29
- def args_from(_context)
30
- raise NotImplemented
31
- end
32
25
  end
33
26
 
34
27
  # @!attribute [r] context
35
- # @return [ActionController::Base]
28
+ # @return [ActionController::Base, ActionView::Base]
36
29
  attr_reader :context
37
30
 
38
31
  # @!attribute [r] user
39
32
  # @return [Object]
40
33
  attr_reader :user
41
34
 
42
- # Empty initialize that accepts all sorts of args
43
- def initialize(*); end
35
+ # @!attribute [r] options
36
+ # @return [Hash]
37
+ attr_reader :options
38
+
39
+ # @param context [ActionController::Base, ActionView::Base]
40
+ # @param options [Hash]
41
+ def initialize(context, **options)
42
+ @context = context
43
+ @options = options
44
+ @user = context.try :wallaby_user
45
+ end
44
46
 
45
47
  # @note It can be overridden in subclasses for customization purpose.
46
48
  # This is the template method to check user's permission for given action on given subject.
@@ -157,12 +157,24 @@ module Wallaby
157
157
  field_names.unshift(primary_key.to_s).uniq
158
158
  end
159
159
 
160
- # Validate presence of a type for given field name
160
+ # Ensure the type is present for given field name
161
161
  # @param type [String, Symbol, nil]
162
162
  # @return [String, Symbol] type
163
163
  # @raise [ArgumentError] when type is nil
164
- def validate_presence_of(field_name, type)
165
- type || raise(::ArgumentError, I18n.t('errors.invalid.type_required', field_name: field_name))
164
+ def ensure_type_is_present(field_name, type, metadata_prefix = '')
165
+ type || raise(::ArgumentError, <<~INSTRUCTION
166
+ The type for field `#{field_name}` is missing in metadata `#{metadata_prefix}_fields`.
167
+ The possible causes are:
168
+
169
+ 1. Check type's value from metadata `#{metadata_prefix}_fields[:#{field_name}][:type]`.
170
+ If it is missing, specify the type as below:
171
+
172
+ #{metadata_prefix}field_name[:#{field_name}][:type] = 'string'
173
+
174
+ 2. If metadata `#{metadata_prefix}_fields` is blank, maybe table hasn't be created yet
175
+ or there is some error in the decorator class declaration.
176
+ INSTRUCTION
177
+ )
166
178
  end
167
179
  end
168
180
  end
@@ -1,72 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wallaby
4
- # Model paginator to provide support for pagination on index page
4
+ # This is the base paginator class to provider pagination for given collection.
5
+ #
6
+ # For best practice, please create an application paginator class (see example)
7
+ # to better control the functions shared between different model paginators.
8
+ # @example Create an application class for Admin Interface usage
9
+ # class Admin::ApplicationPaginator < Wallaby::ModelPaginator
10
+ # base_class!
11
+ # end
5
12
  class ModelPaginator
6
13
  extend Baseable::ClassMethods
7
-
8
- class << self
9
- # @!attribute [w] model_class
10
- attr_writer :model_class
11
-
12
- # @!attribute [r] model_class
13
- # Return associated model class, e.g. return **Product** for **ProductPaginator**.
14
- #
15
- # If Wallaby can't recognise the model class for Paginator, it's required to be configured as below example:
16
- # @example To configure model class
17
- # class Admin::ProductPaginator < Admin::ApplicationPaginator
18
- # self.model_class = Product
19
- # end
20
- # @example To configure model class for version below 5.2.0
21
- # class Admin::ProductPaginator < Admin::ApplicationPaginator
22
- # def self.model_class
23
- # Product
24
- # end
25
- # end
26
- # @return [Class] assoicated model class
27
- # @return [nil] if current class is marked as base class
28
- # @return [nil] if current class is the same as the value of {Wallaby::Configuration::Mapping#model_paginator}
29
- # @return [nil] if current class is {Wallaby::ModelPaginator}
30
- # @return [nil] if assoicated model class is not found
31
- def model_class
32
- return unless self < ModelPaginator
33
- return if base_class? || self == Wallaby.configuration.mapping.model_paginator
34
-
35
- @model_class ||= Map.model_class_map(name.gsub(/(^#{namespace}::)|(Paginator$)/, EMPTY_STRING))
36
- end
37
-
38
- # @!attribute provider_class
39
- # @return [Class] pagination provider class
40
- # @since 5.2.0
41
- attr_accessor :provider_class
42
- end
14
+ base_class!
43
15
 
44
16
  # @!attribute [r] model_class
45
17
  # @return [Class]
46
18
  attr_reader :model_class
47
19
 
48
20
  # @!attribute [r] provider
49
- # @return [Wallaby::ModelServiceProvider]
50
- # @since 5.2.0
21
+ # @return [Wallaby::ModelPaginationProvider] the instance that does the job
22
+ # @since wallaby-5.2.0
51
23
  attr_reader :provider
52
24
 
53
- # During initialization, Wallaby will assign a pagination provider for this paginator
54
- # to carry out the actual execution.
55
- #
56
- # Therefore, all its actions can be completely replaced by user's own implemnetation.
57
25
  # @param model_class [Class]
58
26
  # @param collection [#to_a] a collection of the resources
59
27
  # @param params [ActionController::Parameters]
60
28
  def initialize(model_class, collection, params)
61
29
  @model_class = self.class.model_class || model_class
62
- raise ArgumentError, I18n.t('errors.required', subject: 'model_class') unless @model_class
30
+ raise ArgumentError, 'Please provide a `model_class`.' unless @model_class
63
31
 
64
32
  @collection = collection
65
33
  @params = params
66
34
  @provider = Map.pagination_provider_map(@model_class).new(@collection, @params)
67
35
  end
68
36
 
69
- delegate(*ModelPaginationProvider.instance_methods(false), to: :provider)
70
37
  # @!method paginatable?
71
38
  # (see Wallaby::ModelPaginationProvider#paginatable?)
72
39
 
@@ -111,5 +78,7 @@ module Wallaby
111
78
 
112
79
  # @!method number_of_pages
113
80
  # (see Wallaby::ModelPaginationProvider#number_of_pages)
81
+
82
+ delegate(*ModelPaginationProvider.instance_methods(false), to: :provider)
114
83
  end
115
84
  end
@@ -1,34 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wallaby
4
- # a parser to handle colon query
4
+ # A parser to parse colon query string and return a hash for transformer
5
+ # to consume
5
6
  class Parser < Parslet::Parser
7
+ # @!method parse(str)
8
+ # Parse string into Abstract Syntax Tree for transformer to consume
9
+ # @param str [String]
10
+ # @return [Hash] Abstract Syntax Tree
11
+
12
+ # Case insensitive string match
13
+ # @param str [String]
14
+ def stri(str)
15
+ str.chars.map! { |c| match["#{c.upcase}#{c.downcase}"] }.reduce :>>
16
+ end
17
+
6
18
  root(:statement)
7
- rule(:statement) { expression >> (space >> expression).repeat }
8
- rule(:expression) { colon_query | general_keyword }
19
+ rule(:statement) { expression >> (spaces >> expression).repeat }
20
+ rule(:expression) { colon_query | quoted_string | string | any.repeat(0).as(:null) }
9
21
  rule(:colon_query) do
10
- name.as(:left) >> operator.as(:op) >> keywords.as(:right)
22
+ name.as(:left) >> operator.as(:op) >> values.as(:right)
23
+ end
24
+
25
+ # colon query
26
+ begin
27
+ # name starts with letter
28
+ rule(:name) { letter >> ((colon | spaces).absent? >> any).repeat }
29
+
30
+ # operator starts with colon
31
+ rule(:operator) do
32
+ colon >> (
33
+ (colon | spaces | quote | comma | digit | letter).absent? >> any
34
+ ).repeat(0, 3)
35
+ end
36
+
37
+ # values separated by comma
38
+ rule(:values) { value >> (comma >> value).repeat }
39
+ rule(:value) { quoted_string | data }
40
+ rule(:data) do
41
+ null.as(:null) | boolean.as(:boolean) |
42
+ (spaces.absent? >> comma.absent? >> any).repeat.as(:string)
43
+ end
11
44
  end
12
- rule(:name) { (space.absent? >> colon.absent? >> any).repeat(1) }
13
- rule(:operator) { colon >> match('[^\s\'\"\:\,0-9a-zA-Z]').repeat(0, 3) }
14
- rule(:keywords) { general_keyword >> (comma >> general_keyword).repeat }
15
- rule(:general_keyword) { quoted_keyword | keyword }
16
45
 
17
46
  # basic elements
18
- rule(:quoted_keyword) do
47
+ rule(:quoted_string) do
19
48
  open_quote >>
20
- (close_quote.absent? >> any).repeat.as(:keyword) >>
49
+ (close_quote.absent? >> any).repeat.as(:string) >>
21
50
  close_quote
22
51
  end
23
- rule(:keyword) { ((space | comma).absent? >> any).repeat.as(:keyword) }
52
+ rule(:string) { (spaces.absent? >> any).repeat(1).as(:string) }
24
53
 
25
54
  # atomic entities
55
+ rule(:null) { stri('nil') | stri('null') }
56
+ rule(:boolean) { stri('true') | stri('false') }
57
+ rule(:letter) { match['a-zA-Z'] }
58
+ rule(:digit) { match['0-9'] }
59
+ rule(:dot) { str('.') }
26
60
  rule(:comma) { str(',') }
27
- rule(:space) { match('\s').repeat(1) }
61
+ rule(:spaces) { match['\s'].repeat(1) }
28
62
  rule(:colon) { str(':') }
63
+ rule(:quote) { match['\'\"'] }
29
64
 
30
65
  # open-close elements
31
- rule(:open_quote) { match('[\'\"]').capture(:quote) }
32
- rule(:close_quote) { dynamic { |_src, ctx| str(ctx.captures[:quote]) } }
66
+ rule(:open_quote) { quote.capture(:quoting) }
67
+ rule(:close_quote) { dynamic { |_src, ctx| str(ctx.captures[:quoting]) } }
33
68
  end
34
69
  end
@@ -53,7 +53,7 @@ module Wallaby
53
53
  return model_class unless MODEL_ACTIONS.include? params[:action].to_sym
54
54
  raise ModelNotFound, params[:resources] unless model_class
55
55
  unless Map.mode_map[model_class]
56
- raise UnprocessableEntity, I18n.t('errors.unprocessable_entity.model', model: model_class)
56
+ raise UnprocessableEntity, Locale.t('errors.unprocessable_entity.model', model: model_class)
57
57
  end
58
58
 
59
59
  model_class