admin_it 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +5 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +5 -0
  7. data/README.md +58 -0
  8. data/README_RU.md +52 -0
  9. data/Rakefile +23 -0
  10. data/admin_it.gemspec +38 -0
  11. data/app/assets/fonts/admin_it/FontAwesome.otf +0 -0
  12. data/app/assets/fonts/admin_it/fontawesome-webfont.eot +0 -0
  13. data/app/assets/fonts/admin_it/fontawesome-webfont.svg +414 -0
  14. data/app/assets/fonts/admin_it/fontawesome-webfont.ttf +0 -0
  15. data/app/assets/fonts/admin_it/fontawesome-webfont.woff +0 -0
  16. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.eot +0 -0
  17. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.svg +229 -0
  18. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.ttf +0 -0
  19. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.woff +0 -0
  20. data/app/assets/javascript/admin_it/admin_it.js +89 -0
  21. data/app/assets/javascript/admin_it/bootstrap.min.js +6 -0
  22. data/app/assets/stylesheets/admin_it/admin_it.css +25 -0
  23. data/app/assets/stylesheets/admin_it/bootstrap-theme.min.css +7 -0
  24. data/app/assets/stylesheets/admin_it/bootstrap.min.css +7 -0
  25. data/app/assets/stylesheets/admin_it/font-awesome.min.css +4 -0
  26. data/app/views/admin_it/context/_table.html.slim +38 -0
  27. data/app/views/admin_it/context/_tiles.html.slim +23 -0
  28. data/app/views/admin_it/edit.html.slim +2 -0
  29. data/app/views/admin_it/new.html.slim +2 -0
  30. data/app/views/admin_it/shared/_child.html.slim +15 -0
  31. data/app/views/admin_it/shared/_fields.html.slim +19 -0
  32. data/app/views/admin_it/shared/_filters.html.slim +40 -0
  33. data/app/views/admin_it/shared/_form.html.slim +50 -0
  34. data/app/views/admin_it/shared/_pagination.html.slim +41 -0
  35. data/app/views/admin_it/shared/_toolbar.html.slim +30 -0
  36. data/app/views/admin_it/show.html.slim +6 -0
  37. data/app/views/admin_it/table.html.slim +2 -0
  38. data/app/views/admin_it/tiles.html.slim +2 -0
  39. data/app/views/layouts/admin_it.html.slim +37 -0
  40. data/app/views/layouts/admin_it_dialog.html.slim +8 -0
  41. data/config.ru +7 -0
  42. data/lib/admin_it/config.rb +24 -0
  43. data/lib/admin_it/context/collection_context.rb +239 -0
  44. data/lib/admin_it/context/context.rb +232 -0
  45. data/lib/admin_it/context/show_context.rb +45 -0
  46. data/lib/admin_it/context/single_context.rb +199 -0
  47. data/lib/admin_it/context/table_context.rb +66 -0
  48. data/lib/admin_it/context/tiles_context.rb +31 -0
  49. data/lib/admin_it/context.rb +6 -0
  50. data/lib/admin_it/controller.rb +42 -0
  51. data/lib/admin_it/data/active_record.rb +283 -0
  52. data/lib/admin_it/data/data_behavior.rb +18 -0
  53. data/lib/admin_it/data/hash.rb +27 -0
  54. data/lib/admin_it/data/object.rb +51 -0
  55. data/lib/admin_it/data.rb +24 -0
  56. data/lib/admin_it/definitions.rb +89 -0
  57. data/lib/admin_it/engine.rb +56 -0
  58. data/lib/admin_it/env.rb +37 -0
  59. data/lib/admin_it/errors.rb +5 -0
  60. data/lib/admin_it/field/field.rb +174 -0
  61. data/lib/admin_it/field.rb +1 -0
  62. data/lib/admin_it/filters/entity_filter.rb +4 -0
  63. data/lib/admin_it/filters/field_filter.rb +31 -0
  64. data/lib/admin_it/filters/filter.rb +150 -0
  65. data/lib/admin_it/filters/value_filter.rb +52 -0
  66. data/lib/admin_it/filters.rb +4 -0
  67. data/lib/admin_it/helpers/field.rb +25 -0
  68. data/lib/admin_it/helpers/input.rb +22 -0
  69. data/lib/admin_it/helpers/page.rb +26 -0
  70. data/lib/admin_it/helpers/table.rb +85 -0
  71. data/lib/admin_it/helpers/tiles.rb +17 -0
  72. data/lib/admin_it/helpers/toolbar.rb +40 -0
  73. data/lib/admin_it/helpers/top_menu.rb +39 -0
  74. data/lib/admin_it/helpers.rb +12 -0
  75. data/lib/admin_it/locales/en.yml +14 -0
  76. data/lib/admin_it/locales/ru.yml +14 -0
  77. data/lib/admin_it/renderable.rb +18 -0
  78. data/lib/admin_it/resource.rb +292 -0
  79. data/lib/admin_it/utils.rb +21 -0
  80. data/lib/admin_it/version.rb +5 -0
  81. data/lib/admin_it.rb +19 -0
  82. data/lib/extend_it/array_of.rb +181 -0
  83. data/lib/extend_it/asserts.rb +38 -0
  84. data/lib/extend_it/callbacks.rb +105 -0
  85. data/lib/extend_it/caller.rb +35 -0
  86. data/lib/extend_it/class.rb +47 -0
  87. data/lib/extend_it/dsl.rb +94 -0
  88. data/lib/extend_it/refines.rb +6 -0
  89. data/lib/extend_it/symbolize.rb +39 -0
  90. data/lib/extend_it.rb +5 -0
  91. data/spec/internal/config/database.yml +3 -0
  92. data/spec/internal/config/routes.rb +3 -0
  93. data/spec/internal/db/combustion_test.sqlite +0 -0
  94. data/spec/internal/db/schema.rb +3 -0
  95. data/spec/internal/log/.gitignore +1 -0
  96. data/spec/internal/public/favicon.ico +0 -0
  97. data/spec/lib/context/collection_context_spec.rb +20 -0
  98. data/spec/lib/context/context_spec.rb +28 -0
  99. data/spec/lib/context/single_context_spec.rb +26 -0
  100. data/spec/lib/data/object_spec.rb +29 -0
  101. data/spec/lib/definitions_spec.rb +171 -0
  102. data/spec/lib/field_spec.rb +62 -0
  103. data/spec/lib/resource_spec.rb +39 -0
  104. data/spec/lib/utils_spec.rb +39 -0
  105. data/spec/spec_helper.rb +21 -0
  106. data/spec/support/example_groups/context_example_group.rb +50 -0
  107. data/spec/support/shared_examples/context.rb +2 -0
  108. metadata +337 -0
@@ -0,0 +1,37 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Admin
5
+ meta charset="utf-8"
6
+ = csrf_meta_tags
7
+
8
+ = stylesheet_link_tag 'admin_it/bootstrap.min', \
9
+ media: 'all', \
10
+ 'data-turbolinks-track' => true
11
+ = stylesheet_link_tag 'admin_it/font-awesome.min', \
12
+ media: 'all', \
13
+ 'data-turbolinks-track' => true
14
+ = stylesheet_link_tag 'admin_it/admin_it', \
15
+ media: 'all', \
16
+ 'data-turbolinks-track' => true
17
+ = javascript_include_tag 'jquery', 'jquery_ujs', 'turbolinks'
18
+ = javascript_include_tag 'admin_it/bootstrap.min', \
19
+ 'data-turbolinks-track' => true
20
+ = javascript_include_tag 'admin_it/admin_it'
21
+
22
+ - html = capture do
23
+ body style="padding-top: 70px"
24
+ nav class="navbar navbar-fixed-top navbar-inverse" role="navigation"
25
+ div class="container"
26
+ <!-- top-menu -->
27
+
28
+ div class="container"
29
+ = render File.join(%w(admin_it shared toolbar))
30
+ = render File.join(%w(admin_it shared filters))
31
+ = yield
32
+
33
+ - top_menu_html = capture { context.top_menu.render }
34
+ - toolbar_html = capture { context.toolbar.render }
35
+ = html.gsub('<!-- top-menu -->', top_menu_html) \
36
+ .gsub('<!-- toolbar -->', toolbar_html) \
37
+ .html_safe
@@ -0,0 +1,8 @@
1
+ div class="modal-header"
2
+ h4.modal-title title
3
+ div class="modal-body"
4
+ = yield
5
+ div class="modal-footer"
6
+ button type="button" class="btn btn-default" data-dismiss="modal"
7
+ = t('admin_it.action.close')
8
+ = yield :buttons
data/config.ru ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize!
7
+ run Combustion::Application
@@ -0,0 +1,24 @@
1
+ module AdminIt
2
+ module Config
3
+ def self.root
4
+ @root ||= Rails.root.join('app', 'admin_it')
5
+ end
6
+
7
+ def self.root=(value)
8
+ value = File.expand_path(value, Rails.root)
9
+ fail ArgumentError unless File.directory?(value)
10
+ @root = value
11
+ end
12
+
13
+ def self.controller
14
+ @controller ||= ActionController::Base
15
+ end
16
+
17
+ def self.controller=(value)
18
+ unless value <= ActionController::Base
19
+ fail ArgumentError, 'Wrong controller'
20
+ end
21
+ @controller = value
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,239 @@
1
+ require 'json'
2
+ require File.join %w(extend_it symbolize)
3
+
4
+ using ExtendIt::Symbolize
5
+
6
+ module AdminIt
7
+ class CollectionContext < Context
8
+ extend FiltersHolder
9
+
10
+ @entities_getter = nil
11
+
12
+ class << self
13
+ dsl_block :entities
14
+ dsl_accessor :default_sorting
15
+ dsl_use_hash :filters
16
+ end
17
+
18
+ def self.before_configure
19
+ return if resource.nil?
20
+ visible = fields(scope: :visible).map(&:field_name)
21
+ @filters = Hash[
22
+ resource.filters
23
+ .select { |f| f <= FieldFilter }
24
+ .select { |f| visible.include?(f.field.field_name) }
25
+ .map { |f| [f.filter_name, f] }
26
+ ]
27
+ end
28
+
29
+ def self.filter(name, filter_class: nil, &block)
30
+ assert_symbol(:name)
31
+ if @filters.key?(name)
32
+ filter = @filters[name] = Class.new(@filters[name]) if block_given?
33
+ else
34
+ filter_class = Filter if filter_class.nil? || !filter_class <= Filter
35
+ filter = @filters[name] = filter_class.create(name, self)
36
+ end
37
+ filter.instance_eval(&block) if block_given?
38
+ end
39
+
40
+ def self.entities_getter
41
+ @entities
42
+ end
43
+
44
+ def self.collection?
45
+ true
46
+ end
47
+
48
+ def self.path
49
+ AdminIt::Engine.routes.url_helpers.send("#{resource.plural}_path")
50
+ end
51
+
52
+ def self.sortable_fields(*names)
53
+ names = names.ensure_symbols
54
+ fields.each do |_field|
55
+ _field.sortable = names.include?(_field.field_name)
56
+ end
57
+ end
58
+
59
+ attr_accessor :entity
60
+ class_attr_reader :entities_getter, :path
61
+
62
+ before_load do |store: {}, params: {}|
63
+ self.sorting = store[:sorting] || self.class.default_sorting
64
+ self.sorting = params[:sorting] if params.key?(:sorting)
65
+ self.filters = store[:filters] || []
66
+ self.filters = params[:filters] if params.key?(:filters)
67
+ end
68
+
69
+ after_load do |store: {}, params: {}|
70
+ self.active_filter = params[:active_filter] || store[:active_filter]
71
+ end
72
+
73
+ before_save do |params: {}|
74
+ params.merge!(sorting: sorting.join(';'))
75
+ params.merge!(filters: filters.map { |f| f.dump }.join(';'))
76
+ unless active_filter.nil?
77
+ params.merge!(active_filter: active_filter.name.to_s)
78
+ end
79
+ end
80
+
81
+ def sorting
82
+ return @sorting unless @sorting.nil?
83
+ self.sorting = self.class.default_sorting
84
+ end
85
+
86
+ def sorting=(value)
87
+ value = value.to_s if value.is_a?(Symbol)
88
+ if value.is_a?(Array)
89
+ @sorting = value
90
+ elsif value.is_a?(String) && !value.empty?
91
+ @sorting = [] unless /\W[+\-]\w/ =~ value
92
+ @sorting ||= []
93
+ sortable = self.class.fields(scope: :sortable).map(&:field_name)
94
+ value.split(/[;,|]/).each do |sort|
95
+ sort.strip!
96
+ if sort[0] == '-'
97
+ sort = sort[1..-1] + ':'
98
+ @sorting.delete_if { |s| s.index(sort) == 0 }
99
+ else
100
+ sort = sort[1..-1] if sort[0] == '+'
101
+ sort, order = sort.split(':')
102
+ order = 'asc' if order != 'desc'
103
+ sort = sort.to_sym
104
+ @sorting << "#{sort}:#{order}" if sortable.include?(sort)
105
+ end
106
+ end
107
+ else
108
+ @sorting = []
109
+ end
110
+ end
111
+
112
+ def all_filters
113
+ self.class.filters
114
+ end
115
+
116
+ def filters
117
+ (@filters ||= {}).values
118
+ end
119
+
120
+ def filters=(value)
121
+ if value.is_a?(Array)
122
+ @filters = Hash[
123
+ value.select { |f| f.is_a?(Filter) }.map { |f| [f.name, f] }
124
+ ]
125
+ elsif value.is_a?(Hash)
126
+ self.filters = value.values
127
+ elsif value.is_a?(String)
128
+ @filters ||= {}
129
+ value.strip!
130
+ @filters = {} if value.empty?
131
+ value.split(/[;|]/).each do |str|
132
+ str.strip!
133
+ remove = str[0] == '-'
134
+ str = str[1..-1] if remove
135
+ m = Filter::REGEXP.match(str)
136
+ next if m.nil?
137
+ if remove
138
+ @filters.delete(m[:name].to_sym)
139
+ else
140
+ name = m[:name].to_sym
141
+ if @filters.key?(name)
142
+ @filters[name].change(m[:params])
143
+ else
144
+ @filters[name] = Filter.load(str, self.class.filters)
145
+ end
146
+ end
147
+ end
148
+ else
149
+ @filters = {}
150
+ end
151
+ end
152
+
153
+ def active_filter
154
+ @active_filter ||= filters.empty? ? nil : filters.first
155
+ end
156
+
157
+ def active_filter=(value)
158
+ if value.nil?
159
+ active_filter
160
+ elsif value.is_a?(Class) && value <= Filter
161
+ @active_filter = value
162
+ elsif value.is_a?(String)
163
+ value = value.to_sym
164
+ @active_filter = filters.find { |f| f.name == value }
165
+ end
166
+ end
167
+
168
+ def sortable_fields
169
+ @sortable_fields ||= fields(scope: :sortable)
170
+ end
171
+
172
+ def sortable
173
+ @sortable ||= sortable_fields.map(&:name)
174
+ end
175
+
176
+ def load_context
177
+ collection =
178
+ if entities_getter.nil?
179
+ if controller.respond_to?("#{resource.name}_entities")
180
+ controller.send("#{resource.name}_entities", name)
181
+ elsif controller.respond_to?(:entities)
182
+ controller.entities(entity_class, name)
183
+ else
184
+ load_entities
185
+ end
186
+ else
187
+ entities_getter.call
188
+ end
189
+ filters.each do |filter|
190
+ collection = filter.apply(collection)
191
+ end
192
+ self.entities = collection
193
+ end
194
+
195
+ def entities=(value)
196
+ @entities = value
197
+ end
198
+
199
+ def entities
200
+ self.entity = nil
201
+ collection = self
202
+ @enumerator ||= Enumerator.new do |yielder|
203
+ @entities.each do |v|
204
+ collection.entity = v
205
+ yielder << v
206
+ end
207
+ collection.entity = nil
208
+ end
209
+ end
210
+
211
+ def count
212
+ return @count unless @count.nil?
213
+ # apply filters and limits first
214
+ entities if @enumerator.nil?
215
+ # if @count is not setted yet - calculate it
216
+ @count = entities.count
217
+ end
218
+
219
+ protected
220
+
221
+ def load_entities
222
+ []
223
+ end
224
+ end
225
+
226
+ class ListContext < CollectionContext
227
+ def self.path
228
+ AdminIt::Engine.routes.url_helpers.send("list_#{resource.plural}_path")
229
+ end
230
+
231
+ class << self
232
+ protected
233
+
234
+ def default_icon
235
+ 'bars'
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,232 @@
1
+ require 'uri'
2
+ require File.join %w(extend_it dsl)
3
+ require File.join %w(extend_it array_of)
4
+ require File.join %w(extend_it symbolize)
5
+
6
+ using ExtendIt::Symbolize
7
+
8
+ module AdminIt
9
+ class Context
10
+ extend ExtendIt::Class
11
+ extend ExtendIt::Dsl
12
+ include ExtendIt::Callbacks
13
+ extend DataBehavior
14
+ extend FieldsHolder
15
+
16
+ class << self
17
+ extend ExtendIt::Dsl
18
+
19
+ attr_reader :context_name
20
+ attr_accessor :controller_class
21
+
22
+ dsl_accessor :icon do |value|
23
+ value.nil? ? default_icon : value.to_s
24
+ end
25
+ dsl_use_hash :fields
26
+ end
27
+
28
+ inherited_class_reader :resource, :entity_class
29
+ define_callbacks :initialize, :load, :save
30
+
31
+ def self.create(context_name, _resource, &block)
32
+ fail ArgumentError, 'Wrong resource' unless _resource.is_a?(Resource)
33
+ base = self
34
+ Class.new(base) do
35
+ @resource = _resource
36
+ @context_name = context_name
37
+ @entity_class = @resource.entity_class
38
+
39
+ import_data_module(base)
40
+
41
+ @fields = Hash[
42
+ _resource.fields(scope: :all).map { |f| [f.field_name, f] }
43
+ ]
44
+
45
+ before_configure if respond_to?(:before_configure)
46
+ instance_eval(&block) if block_given?
47
+ after_configure if respond_to?(:after_configure)
48
+ end
49
+ end
50
+
51
+ def self.field(*names, field_class: nil, &block)
52
+ names.ensure_symbols.each do |name|
53
+ if @fields.key?(name)
54
+ field = @fields[name] = Class.new(@fields[name]) if block_given?
55
+ else
56
+ field_class = Field if field_class.nil? || !field_class <= Field
57
+ field = @fields[name] = field_class.create(name, entity_class)
58
+ end
59
+ field.instance_eval(&block) if block_given?
60
+ end
61
+ end
62
+
63
+ def self.collection?
64
+ false
65
+ end
66
+
67
+ def self.single?
68
+ false
69
+ end
70
+
71
+ def self.entity_path?
72
+ false
73
+ end
74
+
75
+ def self.url(context = nil, **params)
76
+ url = context.nil? ? path : context.path
77
+ params = context.nil? ? params : context.url_params(**params)
78
+ unless params.empty?
79
+ url << '?' << params.map { |k, v| "#{k}=#{v}" }.join('&')
80
+ url = URI.escape(url)
81
+ end
82
+ url
83
+ end
84
+
85
+ class << self
86
+ protected
87
+
88
+ def default_icon
89
+ ''
90
+ end
91
+ end
92
+
93
+ class_attr_reader :collection?, :single?, :entity_class, :resource, :icon,
94
+ :entity_path?
95
+ attr_reader :top_menu, :toolbar, :parent, :template, :controller
96
+
97
+ CONTEXT_REGEXP = /\A
98
+ (?<resource_name>[a-zA-Z_][a-zA-Z_0-9]*)\/
99
+ (?<context_name>[a-zA-Z_][a-zA-Z_0-9]*)
100
+ (\((?<identity_value>[a-zA-Z_0-9]+)\))?
101
+ \z/x
102
+
103
+ def initialize(from, params: nil, store: nil, parent_init: false)
104
+ run_callbacks :initialize do
105
+ if from.is_a?(self.class.controller_class)
106
+ @controller = from
107
+ elsif from.is_a?(Context)
108
+ @controller = from.controller
109
+ self.parent = from unless parent_init == true
110
+ params ||= {}
111
+ store ||= {}
112
+ end
113
+
114
+ @fields = self.class.fields(scope: :all).map { |f| f.new }
115
+
116
+ if store.nil?
117
+ session = controller.session
118
+ store = session[:admin_it] ||= {}
119
+ store = store[resource.name] ||= {}
120
+ store = store[name] ||= {}
121
+ end
122
+
123
+ if params.nil?
124
+ params = controller.request.query_parameters
125
+ end
126
+ params = Hash[params.map { |k, v| [k.to_sym, v] }]
127
+
128
+ run_callbacks :load, arguments: { params: params, store: store } do
129
+ load_context unless parent_init == true
130
+ end
131
+ end
132
+ end
133
+
134
+ after_load do |store: {}, params: {}|
135
+ self.layout = params[:layout] if params.key?(:layout)
136
+ if params.key?(:parent)
137
+ self.parent = params[:parent]
138
+ elsif store.key?(:parent)
139
+ self.parent = store[:parent]
140
+ end
141
+ end
142
+
143
+ def name
144
+ @name ||= self.class.context_name
145
+ end
146
+
147
+ def field(name)
148
+ @fields.find { |f| f.name == name }
149
+ end
150
+
151
+ def fields(scope: :visible)
152
+ case scope
153
+ when nil, :all then @fields
154
+ when :visible then @fields.select { |f| f.visible? }
155
+ when :hidden then @fields.select { |f| !f.visible? }
156
+ when :readable then @fields.select { |f| f.readable? }
157
+ when :writable then @fields.select { |f| f.writable? }
158
+ when Field::TYPES then @fields.select { |f| f.type == scope }
159
+ else @fields
160
+ end
161
+ end
162
+
163
+ def save(**params)
164
+ return if controller.nil?
165
+ session = controller.session
166
+ store = session[:admin_it] ||= {}
167
+ store = store[resource.name] ||= {}
168
+ run_callbacks :save, arguments: [{ params: params }] do
169
+ store[name] = params
170
+ end
171
+ end
172
+
173
+ def layout
174
+ @layout ||= ''
175
+ end
176
+
177
+ def layout=(value)
178
+ value = value.to_sym if value.is_a?(String)
179
+ return unless value.is_a?(Symbol)
180
+ @layout = case value
181
+ when :dialog then 'dialog'
182
+ else ''
183
+ end
184
+ end
185
+
186
+ def parent=(value)
187
+ return if value.nil?
188
+ if value.is_a?(Context)
189
+ @parent = value
190
+ elsif value.is_a?(String)
191
+ m = CONTEXT_REGEXP.match(value)
192
+ unless m.nil?
193
+ r = AdminIt.resources[m[:resource_name].downcase.to_sym]
194
+ return if r.nil?
195
+ c = r[m[:context_name].downcase.to_sym]
196
+ return if c.nil?
197
+ @parent = c.new(self, parent_init: true)
198
+ unless m[:identity_value].nil?
199
+ @parent.entity = @parent.load_entity(identity: m[:identity_value])
200
+ end
201
+ end
202
+ else
203
+ @parent = nil
204
+ end
205
+ end
206
+
207
+ def child?
208
+ parent.is_a?(Context)
209
+ end
210
+
211
+ def begin_render(template)
212
+ @template = template
213
+ @toolbar = Helpers::Toolbar.new(template)
214
+ @top_menu = Helpers::TopMenu.new(template, class: 'navbar-nav')
215
+ end
216
+
217
+ def url_params(**params)
218
+ unless @parent.nil?
219
+ params.merge!(parent: @parent.send(:context_param))
220
+ end
221
+ params
222
+ end
223
+
224
+ protected
225
+
226
+ def context_param
227
+ "#{resource.name}/#{name}"
228
+ end
229
+
230
+ def load_context; end
231
+ end
232
+ end
@@ -0,0 +1,45 @@
1
+ module AdminIt
2
+ class ShowContext < SingleContext
3
+ include Identifiable
4
+
5
+ class << self
6
+ include Renderable
7
+
8
+ protected
9
+
10
+ def default_icon
11
+ 'info-circle'
12
+ end
13
+ end
14
+
15
+ def self.entity_path?
16
+ true
17
+ end
18
+
19
+ def self.read(entity = nil, &block)
20
+ if entity.nil?
21
+ @reader = block if block_given?
22
+ elsif !@reader.nil?
23
+ @reader.call(entity)
24
+ end
25
+ end
26
+
27
+ def destroy_entity
28
+ if entity_destroyer.nil?
29
+ if controller.respond_to?("#{resource.name}_destroy")
30
+ controller.send("#{resource.name}_destroy")
31
+ elsif controller.respond_to?(:destroy_entity)
32
+ controller.destroy_entity(entity_class)
33
+ else
34
+ destroy
35
+ end
36
+ else
37
+ entity_destroyer.call(controller)
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def destroy; end
44
+ end
45
+ end