ez-resources 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +106 -0
- data/Rakefile +34 -0
- data/app/cells/ez/resources/application_cell.rb +40 -0
- data/app/cells/ez/resources/collection/table.slim +42 -0
- data/app/cells/ez/resources/collection_cell.rb +115 -0
- data/app/cells/ez/resources/field/show.slim +2 -0
- data/app/cells/ez/resources/field_cell.rb +41 -0
- data/app/cells/ez/resources/form/show.slim +16 -0
- data/app/cells/ez/resources/form_cell.rb +33 -0
- data/app/cells/ez/resources/search/show.slim +20 -0
- data/app/cells/ez/resources/search_cell.rb +43 -0
- data/lib/ez/resources.rb +21 -0
- data/lib/ez/resources/engine.rb +14 -0
- data/lib/ez/resources/manager.rb +114 -0
- data/lib/ez/resources/manager/action.rb +17 -0
- data/lib/ez/resources/manager/config.rb +150 -0
- data/lib/ez/resources/manager/config_store.rb +48 -0
- data/lib/ez/resources/manager/dsl.rb +22 -0
- data/lib/ez/resources/manager/field.rb +46 -0
- data/lib/ez/resources/manager/fields.rb +31 -0
- data/lib/ez/resources/manager/hook.rb +16 -0
- data/lib/ez/resources/manager/hooks.rb +38 -0
- data/lib/ez/resources/railtie.rb +8 -0
- data/lib/ez/resources/version.rb +7 -0
- data/lib/tasks/ez/resources_tasks.rake +5 -0
- metadata +327 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
= div_for 'collection-search-container'
|
2
|
+
= div_for 'collection-search-panel'
|
3
|
+
h4 = t('phrases.search_and_filter')
|
4
|
+
|
5
|
+
= div_for 'collection-search-panel-form'
|
6
|
+
= simple_form_for model.search, url: model.path_for(action: :index), method: :get do |f|
|
7
|
+
- model.collection_columns.each do |field|
|
8
|
+
- if field.searchable && SEARCHABLE_FIELDS.include?(field.type)
|
9
|
+
= div_for 'collection-search-panel-field'
|
10
|
+
= f.input field_name(field),
|
11
|
+
label: field_label(field),
|
12
|
+
required: false,
|
13
|
+
as: cast_field_type(field.type),
|
14
|
+
collection: field.collection,
|
15
|
+
include_blank: true
|
16
|
+
|
17
|
+
= f.submit t('actions.apply'), class: css_for('collection-search-panel-submit-button')
|
18
|
+
|
19
|
+
- if model.params[:q]
|
20
|
+
= link_to t('actions.reset'), model.path_for(action: :index), class: css_for('collection-search-panel-reset-link')
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ez
|
4
|
+
module Resources
|
5
|
+
class SearchCell < ApplicationCell
|
6
|
+
SEARCHABLE_FIELDS = %i[string association select link boolean].freeze
|
7
|
+
|
8
|
+
form
|
9
|
+
|
10
|
+
def cast_field_type(type)
|
11
|
+
case type
|
12
|
+
when :link then :string
|
13
|
+
when :boolean then :select
|
14
|
+
when :association then :string
|
15
|
+
else
|
16
|
+
type
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def field_name(field)
|
21
|
+
if association?(field)
|
22
|
+
search_key(field.name, field.search_suffix, field.options.fetch(:association))
|
23
|
+
else
|
24
|
+
search_key(field.name, field.search_suffix)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def field_label(field)
|
29
|
+
"#{field.title} (#{t "search.suffix.#{field.search_suffix}"})"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def search_key(name, search_suffix, association_prefix = nil)
|
35
|
+
[association_prefix, name, search_suffix].compact.join('_').to_sym
|
36
|
+
end
|
37
|
+
|
38
|
+
def association?(field)
|
39
|
+
field.type == :association && field.options[:association].present?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/ez/resources.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ez/resources/engine'
|
4
|
+
require 'ez/resources/manager'
|
5
|
+
|
6
|
+
require 'ez/configurator'
|
7
|
+
module Ez
|
8
|
+
module Resources
|
9
|
+
include Ez::Configurator
|
10
|
+
|
11
|
+
configure do |config|
|
12
|
+
config.ignore_fields = %w[id created_at updated_at]
|
13
|
+
config.i18n_scope = 'ez_resources'
|
14
|
+
end
|
15
|
+
|
16
|
+
BaseError = Class.new(StandardError)
|
17
|
+
GuessingError = Class.new(BaseError)
|
18
|
+
ConfigurationError = Class.new(BaseError)
|
19
|
+
UnavailableError = Class.new(BaseError)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ez/resources/manager/dsl'
|
4
|
+
require 'ez/resources/manager/config'
|
5
|
+
|
6
|
+
module Ez
|
7
|
+
module Resources
|
8
|
+
module Manager
|
9
|
+
include ::Cell::RailsExtensions::ActionController
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(DSL)
|
13
|
+
|
14
|
+
base.rescue_from UnavailableError do
|
15
|
+
if Ez::Resources.config.ui_failed_hook
|
16
|
+
instance_exec(&Ez::Resources.config.ui_failed_hook)
|
17
|
+
else
|
18
|
+
flash[:alert] = t('messages.unavailable', scope: Ez::Resources.config.i18n_scope)
|
19
|
+
redirect_to '/'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def index
|
25
|
+
Manager::Hooks.can!(:can_list?, ez_resource_config)
|
26
|
+
|
27
|
+
ez_resource_view :collection, ez_resource_config
|
28
|
+
end
|
29
|
+
|
30
|
+
def show
|
31
|
+
Manager::Hooks.can!(:can_read?, ez_resource_config)
|
32
|
+
|
33
|
+
if ez_resource_config.show_action_renders_form?
|
34
|
+
ez_resource_view :form, ez_resource_config
|
35
|
+
else
|
36
|
+
# TODO: render show cell
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def new
|
41
|
+
Manager::Hooks.can!(:can_create?, ez_resource_config)
|
42
|
+
|
43
|
+
ez_resource_view :form, ez_resource_config
|
44
|
+
end
|
45
|
+
|
46
|
+
def create
|
47
|
+
Manager::Hooks.can!(:can_create?, ez_resource_config)
|
48
|
+
|
49
|
+
@ez_resource = ez_resource_config.model.new(ez_resource_params)
|
50
|
+
|
51
|
+
if @ez_resource.save
|
52
|
+
flash[:notice] = t('messages.created',
|
53
|
+
resource_name: ez_resource_config.resource_name,
|
54
|
+
scope: Ez::Resources.config.i18n_scope)
|
55
|
+
|
56
|
+
redirect_to ez_resource_config.path_for(action: :index)
|
57
|
+
else
|
58
|
+
flash[:alert] = t('messages.invalid',
|
59
|
+
resource_name: ez_resource_config.resource_name,
|
60
|
+
scope: Ez::Resources.config.i18n_scope)
|
61
|
+
|
62
|
+
ez_resource_view :form, ez_resource_config
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def edit
|
67
|
+
Manager::Hooks.can!(:can_update?, ez_resource_config, ez_resource_config.data)
|
68
|
+
|
69
|
+
ez_resource_view :form, ez_resource_config
|
70
|
+
end
|
71
|
+
|
72
|
+
def update
|
73
|
+
Manager::Hooks.can!(:can_update?, ez_resource_config, ez_resource_config.data)
|
74
|
+
|
75
|
+
@ez_resource = ez_resource_config.data
|
76
|
+
|
77
|
+
if @ez_resource.update(ez_resource_params)
|
78
|
+
flash[:notice] = t('messages.updated',
|
79
|
+
resource_name: ez_resource_config.resource_name,
|
80
|
+
scope: Ez::Resources.config.i18n_scope)
|
81
|
+
|
82
|
+
redirect_to ez_resource_config.path_for(action: :index)
|
83
|
+
else
|
84
|
+
flash[:alert] = t('messages.invalid',
|
85
|
+
resource_name: ez_resource_config.resource_name,
|
86
|
+
scope: Ez::Resources.config.i18n_scope)
|
87
|
+
|
88
|
+
ez_resource_view :form, ez_resource_config
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# TODO: Later
|
93
|
+
def destroy; end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def ez_resource_view(cell_name, *args)
|
98
|
+
render html: cell("ez/resources/#{cell_name}", *args), layout: true
|
99
|
+
end
|
100
|
+
|
101
|
+
def ez_resource_params
|
102
|
+
params.require(:ez_resource).permit(ez_resource_config.form_fields.map(&:name))
|
103
|
+
end
|
104
|
+
|
105
|
+
def ez_resource_config
|
106
|
+
Config.new(
|
107
|
+
controller: self,
|
108
|
+
dsl_config: self.class.ez_resource_config_store,
|
109
|
+
data: @ez_resource
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ez
|
4
|
+
module Resources
|
5
|
+
module Manager
|
6
|
+
class Action
|
7
|
+
attr_reader :name, :builder, :options
|
8
|
+
|
9
|
+
def initialize(name, builder, options = {})
|
10
|
+
@name = name
|
11
|
+
@builder = builder
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ez
|
4
|
+
module Resources
|
5
|
+
module Manager
|
6
|
+
class Config
|
7
|
+
include Pagy::Backend
|
8
|
+
|
9
|
+
DEFAULT_ACTIONS = %i[index show new create edit update destroy].freeze
|
10
|
+
|
11
|
+
attr_reader :paginator, :search, :controller
|
12
|
+
|
13
|
+
def initialize(controller:, dsl_config:, data: nil)
|
14
|
+
@controller = controller
|
15
|
+
@dsl_config = dsl_config
|
16
|
+
@data = data
|
17
|
+
end
|
18
|
+
|
19
|
+
def data
|
20
|
+
@data ||= case controller.action_name
|
21
|
+
when 'index' then collection
|
22
|
+
when 'new' then new_resource
|
23
|
+
when 'show' then resource
|
24
|
+
when 'edit' then resource
|
25
|
+
when 'update' then resource
|
26
|
+
else
|
27
|
+
raise ConfigurationError, "Invalid action #{controller.action_name}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def total_count
|
32
|
+
@total_count ||= model.count
|
33
|
+
end
|
34
|
+
|
35
|
+
def model
|
36
|
+
@model ||= dsl_config.model || controller_name.classify.constantize
|
37
|
+
rescue NameError
|
38
|
+
raise GuessingError, "Ez::Resources tried to guess model name as #{controller_name.classify} but constant is missing. You can define model class explicitly with :model options"
|
39
|
+
end
|
40
|
+
|
41
|
+
def actions
|
42
|
+
@actions ||= dsl_config.actions || DEFAULT_ACTIONS
|
43
|
+
end
|
44
|
+
|
45
|
+
def show_action_renders_form?
|
46
|
+
@show_action_renders_form ||= dsl_config.show_action_renders_form
|
47
|
+
end
|
48
|
+
|
49
|
+
def hooks
|
50
|
+
@hooks ||= dsl_config.hooks || []
|
51
|
+
end
|
52
|
+
|
53
|
+
def resource_label
|
54
|
+
@resource_label ||= dsl_config.resource_label || :id
|
55
|
+
end
|
56
|
+
|
57
|
+
def resource_name
|
58
|
+
@resource_name ||= controller_name.classify
|
59
|
+
end
|
60
|
+
|
61
|
+
def resources_name
|
62
|
+
@resources_name ||= dsl_config.resources_name || resource_name.pluralize
|
63
|
+
end
|
64
|
+
|
65
|
+
def paginate_collection?
|
66
|
+
@paginate_collection ||= dsl_config.paginate_collection != false
|
67
|
+
end
|
68
|
+
|
69
|
+
def collection_search?
|
70
|
+
@collection_search ||= dsl_config.collection_search != false
|
71
|
+
end
|
72
|
+
|
73
|
+
def collection_views
|
74
|
+
@collection_views ||= dsl_config.collection_views || []
|
75
|
+
end
|
76
|
+
|
77
|
+
def collection_columns
|
78
|
+
@collection_columns ||= dsl_config.collection_columns || model.columns.map do |column|
|
79
|
+
Ez::Resources::Manager::Field.new(
|
80
|
+
name: column.name,
|
81
|
+
title: column.name.to_s.humanize,
|
82
|
+
type: column.sql_type_metadata.type
|
83
|
+
)
|
84
|
+
end.reject { |col| Ez::Resources.config.ignore_fields.include?(col.name) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def collection_actions
|
88
|
+
@colleciton_actions ||= dsl_config.collection_actions || []
|
89
|
+
end
|
90
|
+
|
91
|
+
def form_fields
|
92
|
+
@form_fields ||= dsl_config.form_fields || collection_columns || []
|
93
|
+
end
|
94
|
+
|
95
|
+
def path_for(action:, id: nil, params: nil)
|
96
|
+
if id
|
97
|
+
controller.url_for(action: action, id: id, only_path: true)
|
98
|
+
elsif params
|
99
|
+
controller.url_for(action: action, **params, only_path: true)
|
100
|
+
else
|
101
|
+
controller.url_for(action: action, only_path: true)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def params
|
106
|
+
@params ||= controller.params
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
attr_reader :dsl_config
|
112
|
+
|
113
|
+
def collection
|
114
|
+
return paginated_collection if paginate_collection?
|
115
|
+
|
116
|
+
@collection ||= if dsl_config.collection_query
|
117
|
+
dsl_config.collection_query.call(model, controller)
|
118
|
+
else
|
119
|
+
model.all
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def paginated_collection
|
124
|
+
@search = model.ransack(params[:q])
|
125
|
+
|
126
|
+
@paginated_collection ||= if dsl_config.collection_query
|
127
|
+
pagy, paginated_collection = pagy dsl_config.collection_query.call(search.result, controller)
|
128
|
+
else
|
129
|
+
pagy, paginated_collection = pagy search.result.includes(dsl_config.includes)
|
130
|
+
end
|
131
|
+
|
132
|
+
@paginator = pagy
|
133
|
+
paginated_collection
|
134
|
+
end
|
135
|
+
|
136
|
+
def new_resource
|
137
|
+
@new_resource ||= model.new
|
138
|
+
end
|
139
|
+
|
140
|
+
def resource
|
141
|
+
@resource ||= model.find(controller.params[:id])
|
142
|
+
end
|
143
|
+
|
144
|
+
def controller_name
|
145
|
+
@controller_name ||= controller.controller_name
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ez/resources/manager/fields'
|
4
|
+
require 'ez/resources/manager/hooks'
|
5
|
+
|
6
|
+
module Ez
|
7
|
+
module Resources
|
8
|
+
module Manager
|
9
|
+
class ConfigStore
|
10
|
+
attr_accessor :actions, :model, :paginate_collection, :resource_name, :resource_label, :resources_name,
|
11
|
+
:collection_columns, :collection_query, :collection_search, :show_action_renders_form, :includes,
|
12
|
+
:collection_views
|
13
|
+
|
14
|
+
def collection_columns(&block)
|
15
|
+
if block_given?
|
16
|
+
@collection_columns = Fields.new(&block).fields
|
17
|
+
else
|
18
|
+
@collection_columns
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def collection_actions(&block)
|
23
|
+
if block_given?
|
24
|
+
@collection_actions = Fields.new(&block).actions
|
25
|
+
else
|
26
|
+
@collection_actions
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def form_fields(&block)
|
31
|
+
if block_given?
|
32
|
+
@form_fields = Fields.new(&block).fields
|
33
|
+
else
|
34
|
+
@form_fields
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def hooks(&block)
|
39
|
+
if block_given?
|
40
|
+
@hooks = Hooks.new(&block).hooks
|
41
|
+
else
|
42
|
+
@hooks
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ez/resources/manager/config_store'
|
4
|
+
|
5
|
+
module Ez
|
6
|
+
module Resources
|
7
|
+
module Manager
|
8
|
+
module DSL
|
9
|
+
def ez_resource_config_store
|
10
|
+
@ez_resource_config_store || Ez::Resources::Manager::ConfigStore.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def ez_resource(&block)
|
14
|
+
config = Ez::Resources::Manager::ConfigStore.new
|
15
|
+
block.call(config)
|
16
|
+
|
17
|
+
@ez_resource_config_store = config
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|