wallaby-core 0.1.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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