goodmin 0.0.1

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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +1359 -0
  4. data/Rakefile +56 -0
  5. data/app/assets/stylesheets/goodmin/application.css +75 -0
  6. data/app/controllers/goodmin/application_controller.rb +43 -0
  7. data/app/controllers/goodmin/resource_controller.rb +235 -0
  8. data/app/helpers/goodmin/application_helper.rb +45 -0
  9. data/app/javascript/goodmin/application.js +8 -0
  10. data/app/javascript/goodmin/controllers/batch_actions_controller.js +101 -0
  11. data/app/javascript/goodmin/controllers/datetimepicker_controller.js +24 -0
  12. data/app/javascript/goodmin/controllers/navigation_controller.js +30 -0
  13. data/app/jobs/goodmin/application_job.rb +4 -0
  14. data/app/mailers/goodmin/application_mailer.rb +6 -0
  15. data/app/models/goodmin/application_record.rb +5 -0
  16. data/app/views/goodmin/application/welcome.html.erb +17 -0
  17. data/app/views/goodmin/fields/association/_form.html.erb +19 -0
  18. data/app/views/goodmin/fields/association/_index.html.erb +6 -0
  19. data/app/views/goodmin/fields/association/_show.html.erb +6 -0
  20. data/app/views/goodmin/fields/boolean/_form.html.erb +7 -0
  21. data/app/views/goodmin/fields/boolean/_index.html.erb +1 -0
  22. data/app/views/goodmin/fields/boolean/_show.html.erb +1 -0
  23. data/app/views/goodmin/fields/date/_form.html.erb +6 -0
  24. data/app/views/goodmin/fields/date/_index.html.erb +1 -0
  25. data/app/views/goodmin/fields/date/_show.html.erb +1 -0
  26. data/app/views/goodmin/fields/date_time/_form.html.erb +6 -0
  27. data/app/views/goodmin/fields/date_time/_index.html.erb +1 -0
  28. data/app/views/goodmin/fields/date_time/_show.html.erb +1 -0
  29. data/app/views/goodmin/fields/enum/_index.html.erb +1 -0
  30. data/app/views/goodmin/fields/enum/_show.html.erb +1 -0
  31. data/app/views/goodmin/fields/nested_has_one/_form.html.erb +6 -0
  32. data/app/views/goodmin/fields/nested_has_one/_index.html.erb +1 -0
  33. data/app/views/goodmin/fields/nested_has_one/_show.html.erb +1 -0
  34. data/app/views/goodmin/fields/number/_form.html.erb +5 -0
  35. data/app/views/goodmin/fields/number/_index.html.erb +1 -0
  36. data/app/views/goodmin/fields/number/_show.html.erb +1 -0
  37. data/app/views/goodmin/fields/password/_form.html.erb +5 -0
  38. data/app/views/goodmin/fields/password/_index.html.erb +1 -0
  39. data/app/views/goodmin/fields/password/_show.html.erb +1 -0
  40. data/app/views/goodmin/fields/select/_form.html.erb +5 -0
  41. data/app/views/goodmin/fields/select/_index.html.erb +1 -0
  42. data/app/views/goodmin/fields/select/_show.html.erb +1 -0
  43. data/app/views/goodmin/fields/string/_form.html.erb +5 -0
  44. data/app/views/goodmin/fields/string/_index.html.erb +1 -0
  45. data/app/views/goodmin/fields/string/_show.html.erb +1 -0
  46. data/app/views/goodmin/fields/text/_form.html.erb +5 -0
  47. data/app/views/goodmin/fields/text/_index.html.erb +1 -0
  48. data/app/views/goodmin/fields/text/_show.html.erb +1 -0
  49. data/app/views/goodmin/resource/_actions.html.erb +9 -0
  50. data/app/views/goodmin/resource/_batch_actions.html.erb +12 -0
  51. data/app/views/goodmin/resource/_breadcrumb.html.erb +33 -0
  52. data/app/views/goodmin/resource/_breadcrumb_actions.html.erb +41 -0
  53. data/app/views/goodmin/resource/_button_actions.html.erb +3 -0
  54. data/app/views/goodmin/resource/_errors.html.erb +9 -0
  55. data/app/views/goodmin/resource/_export_actions.html.erb +15 -0
  56. data/app/views/goodmin/resource/_filters.html.erb +22 -0
  57. data/app/views/goodmin/resource/_form.html.erb +26 -0
  58. data/app/views/goodmin/resource/_pagination.html.erb +40 -0
  59. data/app/views/goodmin/resource/_scopes.html.erb +14 -0
  60. data/app/views/goodmin/resource/_table.html.erb +45 -0
  61. data/app/views/goodmin/resource/columns/_actions.html.erb +28 -0
  62. data/app/views/goodmin/resource/edit.html.erb +5 -0
  63. data/app/views/goodmin/resource/index.csv.csvbuilder +5 -0
  64. data/app/views/goodmin/resource/index.html.erb +10 -0
  65. data/app/views/goodmin/resource/index.json.jbuilder +3 -0
  66. data/app/views/goodmin/resource/new.html.erb +5 -0
  67. data/app/views/goodmin/resource/show.html.erb +11 -0
  68. data/app/views/goodmin/resource/show.json.jbuilder +1 -0
  69. data/app/views/goodmin/sessions/new.html.erb +11 -0
  70. data/app/views/goodmin/shared/_navigation.html.erb +0 -0
  71. data/app/views/goodmin/shared/_navigation_aside.html.erb +7 -0
  72. data/app/views/layouts/goodmin/_content.html.erb +13 -0
  73. data/app/views/layouts/goodmin/_layout.html.erb +22 -0
  74. data/app/views/layouts/goodmin/application.html.erb +28 -0
  75. data/app/views/layouts/goodmin/login.html.erb +18 -0
  76. data/config/importmap.rb +5 -0
  77. data/config/locales/en.yml +49 -0
  78. data/config/locales/pl-BR.yml +49 -0
  79. data/config/locales/pt-BR.yml +49 -0
  80. data/config/locales/sv.yml +49 -0
  81. data/config/routes.rb +3 -0
  82. data/lib/generators/goodmin/authentication/authentication_generator.rb +41 -0
  83. data/lib/generators/goodmin/authentication/templates/sessions_controller.rb +9 -0
  84. data/lib/generators/goodmin/install/install_generator.rb +41 -0
  85. data/lib/generators/goodmin/policy/policy_generator.rb +7 -0
  86. data/lib/generators/goodmin/policy/templates/policy.rb +23 -0
  87. data/lib/generators/goodmin/resource/resource_generator.rb +31 -0
  88. data/lib/generators/goodmin/resource/templates/resource.rb +25 -0
  89. data/lib/generators/goodmin/resource/templates/resource_controller.rb +9 -0
  90. data/lib/generators/goodmin/resource/templates/resource_model.rb +4 -0
  91. data/lib/generators/goodmin/resource/templates/resource_service.rb +23 -0
  92. data/lib/goodmin/authentication/sessions_controller.rb +46 -0
  93. data/lib/goodmin/authentication/user.rb +27 -0
  94. data/lib/goodmin/authentication.rb +35 -0
  95. data/lib/goodmin/authorization/policy.rb +41 -0
  96. data/lib/goodmin/authorization.rb +69 -0
  97. data/lib/goodmin/engine.rb +30 -0
  98. data/lib/goodmin/fields/association.rb +62 -0
  99. data/lib/goodmin/fields/base.rb +57 -0
  100. data/lib/goodmin/fields/boolean.rb +6 -0
  101. data/lib/goodmin/fields/date.rb +6 -0
  102. data/lib/goodmin/fields/date_time.rb +6 -0
  103. data/lib/goodmin/fields/enum.rb +15 -0
  104. data/lib/goodmin/fields/nested_has_one.rb +41 -0
  105. data/lib/goodmin/fields/number.rb +6 -0
  106. data/lib/goodmin/fields/password.rb +6 -0
  107. data/lib/goodmin/fields/select.rb +19 -0
  108. data/lib/goodmin/fields/string.rb +6 -0
  109. data/lib/goodmin/fields/text.rb +6 -0
  110. data/lib/goodmin/generators/base.rb +49 -0
  111. data/lib/goodmin/generators/named_base.rb +31 -0
  112. data/lib/goodmin/helpers/application.rb +47 -0
  113. data/lib/goodmin/helpers/batch_actions.rb +21 -0
  114. data/lib/goodmin/helpers/filters.rb +123 -0
  115. data/lib/goodmin/helpers/forms.rb +42 -0
  116. data/lib/goodmin/helpers/navigation.rb +52 -0
  117. data/lib/goodmin/helpers/tables.rb +25 -0
  118. data/lib/goodmin/helpers/translations.rb +19 -0
  119. data/lib/goodmin/paginator.rb +55 -0
  120. data/lib/goodmin/resolver.rb +141 -0
  121. data/lib/goodmin/resources/attribute.rb +46 -0
  122. data/lib/goodmin/resources/form_builder.rb +96 -0
  123. data/lib/goodmin/resources/form_component.rb +65 -0
  124. data/lib/goodmin/resources/form_components/col.rb +33 -0
  125. data/lib/goodmin/resources/form_components/row.rb +25 -0
  126. data/lib/goodmin/resources/form_components/section.rb +51 -0
  127. data/lib/goodmin/resources/form_components/tab.rb +49 -0
  128. data/lib/goodmin/resources/resource/associations.rb +23 -0
  129. data/lib/goodmin/resources/resource/batch_actions.rb +56 -0
  130. data/lib/goodmin/resources/resource/filters.rb +44 -0
  131. data/lib/goodmin/resources/resource/ordering.rb +41 -0
  132. data/lib/goodmin/resources/resource/pagination.rb +22 -0
  133. data/lib/goodmin/resources/resource/scopes.rb +61 -0
  134. data/lib/goodmin/resources/resource.rb +199 -0
  135. data/lib/goodmin/resources/resource_controller/batch_actions.rb +49 -0
  136. data/lib/goodmin/resources/resource_service/associations.rb +23 -0
  137. data/lib/goodmin/resources/resource_service/batch_actions.rb +52 -0
  138. data/lib/goodmin/resources/resource_service/filters.rb +44 -0
  139. data/lib/goodmin/resources/resource_service/ordering.rb +41 -0
  140. data/lib/goodmin/resources/resource_service/pagination.rb +22 -0
  141. data/lib/goodmin/resources/resource_service/scopes.rb +61 -0
  142. data/lib/goodmin/resources/resource_service.rb +199 -0
  143. data/lib/goodmin/service_locator.rb +25 -0
  144. data/lib/goodmin/version.rb +3 -0
  145. data/lib/goodmin.rb +44 -0
  146. data/lib/tasks/goodmin_tasks.rake +4 -0
  147. metadata +461 -0
@@ -0,0 +1,41 @@
1
+ module Goodmin
2
+ module Resources
3
+ module Resource
4
+ module Ordering
5
+ extend ActiveSupport::Concern
6
+
7
+ def apply_order(order_param, resources)
8
+ if order_param.present? && order_column_method?(order_column(order_param))
9
+ send("order_by_#{order_column(order_param)}", resources, order_direction(order_param))
10
+ elsif order_param.present? && order_column_column?(order_column(order_param))
11
+ resources.order("#{resource_class.table_name}.#{order_column(order_param)} #{order_direction(order_param)}")
12
+ else
13
+ resources
14
+ end
15
+ end
16
+
17
+ def orderable_column?(column)
18
+ order_column_method?(column) || order_column_column?(column)
19
+ end
20
+
21
+ protected
22
+
23
+ def order_column_method?(column)
24
+ respond_to?("order_by_#{column}")
25
+ end
26
+
27
+ def order_column_column?(column)
28
+ resource_class.column_names.include?(column)
29
+ end
30
+
31
+ def order_column(order_param)
32
+ order_param.rpartition("_").first
33
+ end
34
+
35
+ def order_direction(order_param)
36
+ order_param.rpartition("_").last == "asc" ? "asc" : "desc"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ module Goodmin
2
+ module Resources
3
+ module Resource
4
+ module Pagination
5
+ extend ActiveSupport::Concern
6
+
7
+ def apply_pagination(page_param, resources)
8
+ @paginator = Paginator.new(resources, per_page: per_page, current_page: page_param)
9
+ @paginator.paginate
10
+ end
11
+
12
+ def paginator
13
+ @paginator
14
+ end
15
+
16
+ def per_page
17
+ 25
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,61 @@
1
+ module Goodmin
2
+ module Resources
3
+ module Resource
4
+ module Scopes
5
+ extend ActiveSupport::Concern
6
+
7
+ delegate :scope_map, to: "self.class"
8
+
9
+ def apply_scope(scope_param, resources)
10
+ return resources if scope_map.empty?
11
+
12
+ self.scope = scope_param
13
+
14
+ if scope && scope_map.key?(scope.to_sym)
15
+ send("scope_#{@scope}", resources)
16
+ else
17
+ fail NotImplementedError, "Scope #{@scope} not implemented"
18
+ end
19
+ end
20
+
21
+ def scope=(scope)
22
+ @scope = scope.blank? ? default_scope : scope
23
+ end
24
+
25
+ def scope
26
+ @scope
27
+ end
28
+
29
+ def scoped_by?(name)
30
+ @scope == name.to_s
31
+ end
32
+
33
+ def scope_count(scope)
34
+ send("scope_#{scope}", resources_relation).count
35
+ end
36
+
37
+ protected
38
+
39
+ def default_scope
40
+ scope = scope_map.find -> { scope_map.first } do |_key, value|
41
+ value[:default] == true
42
+ end
43
+
44
+ scope ? scope[0].to_s : nil
45
+ end
46
+
47
+ module ClassMethods
48
+ def scope_map
49
+ @scope_map ||= {}
50
+ end
51
+
52
+ def scope(attr, options = {})
53
+ scope_map[attr] = {
54
+ default: false
55
+ }.merge(options)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,199 @@
1
+ require "goodmin/resources/attribute"
2
+ require "goodmin/resources/form_builder"
3
+ require "goodmin/resources/resource/associations"
4
+ require "goodmin/resources/resource/batch_actions"
5
+ require "goodmin/resources/resource/filters"
6
+ require "goodmin/resources/resource/ordering"
7
+ require "goodmin/resources/resource/pagination"
8
+ require "goodmin/resources/resource/scopes"
9
+
10
+ module Goodmin
11
+ module Resources
12
+ module Resource
13
+ extend ActiveSupport::Concern
14
+
15
+ class AttributeBuilder
16
+ attr_reader :attributes
17
+
18
+ def initialize
19
+ @attributes = []
20
+ end
21
+
22
+ def attribute(name, field: nil, **options)
23
+ @attributes << Attribute.new(name, field_class: field, **options)
24
+ end
25
+ end
26
+
27
+ include Associations
28
+ include BatchActions
29
+ include Filters
30
+ include Ordering
31
+ include Pagination
32
+ include Scopes
33
+
34
+ attr_reader :options
35
+
36
+ def initialize(options = {})
37
+ @options = options
38
+ end
39
+
40
+ def resource_class
41
+ self.class.name.delete_prefix("Goodmin::Resources::").chomp("Resource").constantize
42
+ end
43
+
44
+ def resources_relation
45
+ if options[:resource_parent].present?
46
+ parent = options[:resource_parent]
47
+ association_name = resource_class.name.underscore.pluralize.to_sym
48
+ reflection = parent.class.reflect_on_association(association_name)
49
+
50
+ if reflection && (reflection.macro == :has_and_belongs_to_many ||
51
+ (reflection.macro == :has_many && reflection.options[:through].present?))
52
+ parent.send(association_name)
53
+ else
54
+ resource_class.where(parent.class.name.underscore => parent)
55
+ end
56
+ else
57
+ resource_class.all
58
+ end
59
+ end
60
+
61
+ def resources(params)
62
+ apply_pagination(
63
+ params[:page], apply_order(
64
+ params[:order], apply_filters(
65
+ params[:filter], apply_scope(
66
+ params[:scope], resources_relation
67
+ )
68
+ )
69
+ )
70
+ )
71
+ end
72
+
73
+ def find_resource(id)
74
+ resources_relation.find(id)
75
+ end
76
+
77
+ def build_resource(params)
78
+ resources_relation.new(params)
79
+ end
80
+
81
+ def create_resource(resource)
82
+ resource.save
83
+ end
84
+
85
+ def update_resource(resource, params)
86
+ resource.update(params)
87
+ end
88
+
89
+ def destroy_resource(resource)
90
+ resource.destroy
91
+ end
92
+
93
+ def attrs_for_index
94
+ self.class.attrs_for_index
95
+ end
96
+
97
+ def attrs_for_show
98
+ self.class.attrs_for_show
99
+ end
100
+
101
+ def attrs_for_form
102
+ self.class.attrs_for_form
103
+ end
104
+
105
+ def form_nodes
106
+ self.class.form_nodes
107
+ end
108
+
109
+ def form_tabs
110
+ self.class.form_tabs
111
+ end
112
+
113
+ def attrs_for_export
114
+ self.class.attrs_for_export
115
+ end
116
+
117
+ def display_name(record)
118
+ record.to_s
119
+ end
120
+
121
+ def option_text_for_association(attribute)
122
+ self.class.association_option_texts[attribute] || :to_s
123
+ end
124
+
125
+ module ClassMethods
126
+ def index(&block)
127
+ if block_given?
128
+ builder = AttributeBuilder.new
129
+ builder.instance_eval(&block)
130
+ @attrs_for_index = builder.attributes
131
+ end
132
+ @attrs_for_index || []
133
+ end
134
+
135
+ def show(&block)
136
+ if block_given?
137
+ builder = AttributeBuilder.new
138
+ builder.instance_eval(&block)
139
+ @attrs_for_show = builder.attributes
140
+ end
141
+ @attrs_for_show || []
142
+ end
143
+
144
+ def form(&block)
145
+ if block_given?
146
+ builder = FormBuilder.new
147
+ builder.instance_eval(&block)
148
+ @form_nodes = builder.nodes
149
+ @attrs_for_form = builder.attributes
150
+ @form_tabs = builder.tabs
151
+ end
152
+ @attrs_for_form || []
153
+ end
154
+
155
+ def export(&block)
156
+ if block_given?
157
+ builder = AttributeBuilder.new
158
+ builder.instance_eval(&block)
159
+ @attrs_for_export = builder.attributes
160
+ end
161
+ @attrs_for_export || []
162
+ end
163
+
164
+ def attrs_for_index
165
+ @attrs_for_index || []
166
+ end
167
+
168
+ def attrs_for_show
169
+ @attrs_for_show || []
170
+ end
171
+
172
+ def attrs_for_form
173
+ @attrs_for_form || []
174
+ end
175
+
176
+ def form_nodes
177
+ @form_nodes || attrs_for_form.map { |attr| AttributeNode.new(attr) }
178
+ end
179
+
180
+ def form_tabs
181
+ @form_tabs || []
182
+ end
183
+
184
+ def attrs_for_export
185
+ @attrs_for_export || []
186
+ end
187
+
188
+ def association_option_text(attribute, method_name)
189
+ @association_option_texts ||= {}
190
+ @association_option_texts[attribute] = method_name
191
+ end
192
+
193
+ def association_option_texts
194
+ @association_option_texts || {}
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,49 @@
1
+ module Goodmin
2
+ module Resources
3
+ module ResourceController
4
+ module BatchActions
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ prepend_before_action :perform_batch_action, only: :update
9
+ end
10
+
11
+ protected
12
+
13
+ def perform_batch_action
14
+ return unless params[:batch_action].present?
15
+
16
+ set_resource_service
17
+ set_resource_class
18
+
19
+ if authorization_enabled?
20
+ authorize(batch_action_records, "batch_action_#{params[:batch_action]}?")
21
+ end
22
+
23
+ if @resource_service.batch_action(params[:batch_action], batch_action_records)
24
+ flash[:notice] = translate_scoped(
25
+ "flash.batch_action", number_of_records: batch_action_ids.length,
26
+ resource: @resource_class.model_name.human(count: batch_action_ids.length)
27
+ )
28
+ flash[:updated_ids] = batch_action_ids
29
+
30
+ if respond_to?("redirect_after_batch_action_#{params[:batch_action]}", true)
31
+ redirect_to send("redirect_after_batch_action_#{params[:batch_action]}")
32
+ return
33
+ end
34
+ end
35
+
36
+ redirect_back(fallback_location: root_path)
37
+ end
38
+
39
+ def batch_action_ids
40
+ @_batch_action_ids ||= params[:id].split(",").map(&:to_i)
41
+ end
42
+
43
+ def batch_action_records
44
+ @_batch_action_records ||= @resource_class.where(id: batch_action_ids)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ module Goodmin
2
+ module Resources
3
+ module ResourceService
4
+ module Associations
5
+ extend ActiveSupport::Concern
6
+
7
+ delegate :has_many_map, to: "self.class"
8
+
9
+ module ClassMethods
10
+ def has_many_map
11
+ @has_many_map ||= {}
12
+ end
13
+
14
+ def has_many(attr, options = {})
15
+ has_many_map[attr] = {
16
+ class_name: attr.to_s.singularize.classify
17
+ }.merge(options)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ module Goodmin
2
+ module Resources
3
+ module ResourceService
4
+ module BatchActions
5
+ extend ActiveSupport::Concern
6
+
7
+ delegate :batch_action_map, to: "self.class"
8
+
9
+ def batch_action(action, records)
10
+ if batch_action?(action)
11
+ send("batch_action_#{action}", records)
12
+ true
13
+ else
14
+ false
15
+ end
16
+ end
17
+
18
+ def batch_action?(action)
19
+ batch_action_map.key?(action.to_sym)
20
+ end
21
+
22
+ def include_batch_action?(action)
23
+ options = batch_action_map[action.to_sym]
24
+
25
+ (options[:only].nil? && options[:except].nil?) ||
26
+ (options[:only] && options[:only].include?(scope.to_sym)) ||
27
+ (options[:except] && !options[:except].include?(scope.to_sym))
28
+ end
29
+
30
+ def include_batch_actions?
31
+ batch_action_map.keys.any? do |action|
32
+ include_batch_action?(action)
33
+ end
34
+ end
35
+
36
+ module ClassMethods
37
+ def batch_action_map
38
+ @batch_action_map ||= {}
39
+ end
40
+
41
+ def batch_action(attr, options = {})
42
+ batch_action_map[attr] = {
43
+ only: nil,
44
+ except: nil,
45
+ confirm: false
46
+ }.merge(options)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ module Goodmin
2
+ module Resources
3
+ module ResourceService
4
+ module Filters
5
+ extend ActiveSupport::Concern
6
+
7
+ delegate :filter_map, to: "self.class"
8
+
9
+ def apply_filters(filter_params, resources)
10
+ if filter_params.present?
11
+ filter_params.each do |name, value|
12
+ if apply_filter?(name, value)
13
+ resources = send("filter_#{name}", resources, value)
14
+ end
15
+ end
16
+ end
17
+ resources
18
+ end
19
+
20
+ private
21
+
22
+ def apply_filter?(name, value)
23
+ return false if value == [""]
24
+ filter_map.key?(name.to_sym) && value.present?
25
+ end
26
+
27
+ module ClassMethods
28
+ def filter_map
29
+ @filter_map ||= {}
30
+ end
31
+
32
+ def filter(attr, options = {})
33
+ filter_map[attr] = {
34
+ as: :string,
35
+ option_text: "to_s",
36
+ option_value: "id",
37
+ collection: nil
38
+ }.merge(options)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,41 @@
1
+ module Goodmin
2
+ module Resources
3
+ module ResourceService
4
+ module Ordering
5
+ extend ActiveSupport::Concern
6
+
7
+ def apply_order(order_param, resources)
8
+ if order_param.present? && order_column_method?(order_column(order_param))
9
+ send("order_by_#{order_column(order_param)}", resources, order_direction(order_param))
10
+ elsif order_param.present? && order_column_column?(order_column(order_param))
11
+ resources.order("#{resource_class.table_name}.#{order_column(order_param)} #{order_direction(order_param)}")
12
+ else
13
+ resources
14
+ end
15
+ end
16
+
17
+ def orderable_column?(column)
18
+ order_column_method?(column) || order_column_column?(column)
19
+ end
20
+
21
+ protected
22
+
23
+ def order_column_method?(column)
24
+ respond_to?("order_by_#{column}")
25
+ end
26
+
27
+ def order_column_column?(column)
28
+ resource_class.column_names.include?(column)
29
+ end
30
+
31
+ def order_column(order_param)
32
+ order_param.rpartition("_").first
33
+ end
34
+
35
+ def order_direction(order_param)
36
+ order_param.rpartition("_").last == "asc" ? "asc" : "desc"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ module Goodmin
2
+ module Resources
3
+ module ResourceService
4
+ module Pagination
5
+ extend ActiveSupport::Concern
6
+
7
+ def apply_pagination(page_param, resources)
8
+ @paginator = Paginator.new(resources, per_page: per_page, current_page: page_param)
9
+ @paginator.paginate
10
+ end
11
+
12
+ def paginator
13
+ @paginator
14
+ end
15
+
16
+ def per_page
17
+ 25
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,61 @@
1
+ module Goodmin
2
+ module Resources
3
+ module ResourceService
4
+ module Scopes
5
+ extend ActiveSupport::Concern
6
+
7
+ delegate :scope_map, to: "self.class"
8
+
9
+ def apply_scope(scope_param, resources)
10
+ return resources if scope_map.empty?
11
+
12
+ self.scope = scope_param
13
+
14
+ if scope && scope_map.key?(scope.to_sym)
15
+ send("scope_#{@scope}", resources)
16
+ else
17
+ fail NotImplementedError, "Scope #{@scope} not implemented"
18
+ end
19
+ end
20
+
21
+ def scope=(scope)
22
+ @scope = scope.blank? ? default_scope : scope
23
+ end
24
+
25
+ def scope
26
+ @scope
27
+ end
28
+
29
+ def scoped_by?(name)
30
+ @scope == name.to_s
31
+ end
32
+
33
+ def scope_count(scope)
34
+ send("scope_#{scope}", resources_relation).count
35
+ end
36
+
37
+ protected
38
+
39
+ def default_scope
40
+ scope = scope_map.find -> { scope_map.first } do |_key, value|
41
+ value[:default] == true
42
+ end
43
+
44
+ scope ? scope[0].to_s : nil
45
+ end
46
+
47
+ module ClassMethods
48
+ def scope_map
49
+ @scope_map ||= {}
50
+ end
51
+
52
+ def scope(attr, options = {})
53
+ scope_map[attr] = {
54
+ default: false
55
+ }.merge(options)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end