ez-resources 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cells-rails'
4
+ require 'cells-slim'
5
+ require 'simple_form'
6
+ require 'pagy'
7
+ require 'ransack'
8
+
9
+ module Ez
10
+ module Resources
11
+ class Engine < ::Rails::Engine
12
+ end
13
+ end
14
+ 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