adminable 0.0.5 → 0.0.6
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.
- checksums.yaml +4 -4
- data/README.md +49 -62
- data/app/controllers/adminable/resources_controller.rb +19 -28
- data/app/views/adminable/resources/_form.html.haml +2 -2
- data/app/views/adminable/resources/_search.html.haml +3 -3
- data/app/views/adminable/resources/form/_belongs_to.html.haml +5 -5
- data/app/views/adminable/resources/form/_boolean.html.haml +4 -4
- data/app/views/adminable/resources/form/_date.html.haml +3 -3
- data/app/views/adminable/resources/form/_datetime.html.haml +3 -3
- data/app/views/adminable/resources/form/_decimal.html.haml +3 -3
- data/app/views/adminable/resources/form/_float.html.haml +3 -3
- data/app/views/adminable/resources/form/_has_many.html.haml +5 -5
- data/app/views/adminable/resources/form/_integer.html.haml +3 -3
- data/app/views/adminable/resources/form/_string.html.haml +3 -3
- data/app/views/adminable/resources/form/_text.html.haml +4 -4
- data/app/views/adminable/resources/form/_time.html.haml +3 -3
- data/app/views/adminable/resources/form/_timestamp.html.haml +3 -3
- data/app/views/adminable/resources/index.html.haml +10 -10
- data/app/views/adminable/shared/_label.html.haml +4 -4
- data/lib/adminable.rb +15 -16
- data/lib/adminable/errors.rb +5 -2
- data/lib/adminable/field_collector.rb +53 -0
- data/lib/adminable/{attributes → fields}/base.rb +21 -13
- data/lib/adminable/fields/belongs_to.rb +9 -0
- data/lib/adminable/fields/boolean.rb +6 -0
- data/lib/adminable/fields/date.rb +6 -0
- data/lib/adminable/fields/datetime.rb +6 -0
- data/lib/adminable/fields/decimal.rb +6 -0
- data/lib/adminable/fields/float.rb +6 -0
- data/lib/adminable/fields/has_many.rb +13 -0
- data/lib/adminable/fields/integer.rb +6 -0
- data/lib/adminable/fields/string.rb +6 -0
- data/lib/adminable/fields/text.rb +6 -0
- data/lib/adminable/fields/time.rb +6 -0
- data/lib/adminable/fields/timestamp.rb +6 -0
- data/lib/adminable/presenters/fields.rb +17 -0
- data/lib/adminable/resource.rb +5 -17
- data/lib/adminable/version.rb +1 -1
- data/lib/generators/adminable/resource/templates/resource_controller.rb.erb +6 -1
- data/lib/generators/adminable/resource_generator.rb +9 -1
- metadata +17 -17
- data/lib/adminable/attributes/association.rb +0 -14
- data/lib/adminable/attributes/collection.rb +0 -116
- data/lib/adminable/attributes/types/belongs_to.rb +0 -11
- data/lib/adminable/attributes/types/boolean.rb +0 -8
- data/lib/adminable/attributes/types/date.rb +0 -8
- data/lib/adminable/attributes/types/datetime.rb +0 -8
- data/lib/adminable/attributes/types/decimal.rb +0 -8
- data/lib/adminable/attributes/types/float.rb +0 -8
- data/lib/adminable/attributes/types/has_many.rb +0 -15
- data/lib/adminable/attributes/types/integer.rb +0 -8
- data/lib/adminable/attributes/types/string.rb +0 -8
- data/lib/adminable/attributes/types/text.rb +0 -8
- data/lib/adminable/attributes/types/time.rb +0 -8
- data/lib/adminable/attributes/types/timestamp.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ddb7fa61bee7a687cdc0c39c37990d5bb521304
|
4
|
+
data.tar.gz: 39e26845450a55d099ed58a2abc9feb49c151427
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 030afe5a4016c70a0ecbd93ab4d42872b2b14307f697d2268f36707c400b8cd0d6a28a93baa939dd4de0ce0cf6b2d89260de5ae618089fc5ee97c0b6fd32d85e
|
7
|
+
data.tar.gz: be70b417ed4ef1804fd51549fda2443645be24f6cf3463cb9ffbbdafe3df0eef12dc951ec4c0597ce56592b86e439fec6e64c2c06b2d4c0543020b6f2d66f948
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@ Simple admin interface for Ruby on Rails applications.
|
|
11
11
|
|
12
12
|
## Features
|
13
13
|
|
14
|
-
* Built with common Rails controllers
|
14
|
+
* Built with common Rails controllers and views without DSL.
|
15
15
|
* Supports namespaced models.
|
16
16
|
* Has simple search with [Ransack](https://github.com/activerecord-hackery/ransack).
|
17
17
|
* Uses [Bootstrap](https://github.com/twbs/bootstrap) 4.0.
|
@@ -43,7 +43,7 @@ $ gem install adminable
|
|
43
43
|
|
44
44
|
## Getting Started
|
45
45
|
|
46
|
-
First things first. Add routes and create `application_controller.rb` class using generator:
|
46
|
+
First things first. Add routes and create `adminable/application_controller.rb` class using generator:
|
47
47
|
|
48
48
|
```bash
|
49
49
|
rails g adminable:install
|
@@ -51,93 +51,80 @@ rails g adminable:install
|
|
51
51
|
# => insert config/routes.rb
|
52
52
|
```
|
53
53
|
|
54
|
-
|
54
|
+
### Generating Resources
|
55
55
|
|
56
|
-
|
56
|
+
Assume you have model `User`, then run:
|
57
57
|
|
58
58
|
```bash
|
59
|
-
rails g adminable:resource
|
59
|
+
rails g adminable:resource users
|
60
60
|
# => create app/controllers/adminable/users_controller.rb
|
61
61
|
```
|
62
62
|
|
63
63
|
For namespaced models, like `Blog::Post`, use:
|
64
64
|
|
65
65
|
```bash
|
66
|
-
rails g adminable:resource blog/
|
66
|
+
rails g adminable:resource blog/posts
|
67
67
|
# => create app/controllers/adminable/blog/posts_controller.rb
|
68
68
|
```
|
69
69
|
|
70
|
-
|
70
|
+
### Customizing Fields
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
##### For existing attributes
|
72
|
+
Change fields as you like inside `fields` method array:
|
75
73
|
|
76
74
|
```ruby
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
75
|
+
class Adminable::Blog::PostsController < Adminable::ResourcesController
|
76
|
+
def fields
|
77
|
+
[
|
78
|
+
Adminable::Fields::String.new(:title),
|
79
|
+
Adminable::Fields::Text.new(:body),
|
80
|
+
Adminable::Fields::Float.new(:rating, form: false),
|
81
|
+
Adminable::Fields::Boolean.new(:published),
|
82
|
+
Adminable::Fields::BelongsTo.new(:user),
|
83
|
+
Adminable::Fields::HasMany.new(:blog_comments)
|
84
|
+
]
|
85
|
+
end
|
86
|
+
end
|
84
87
|
```
|
85
88
|
|
86
|
-
|
87
|
-
|
88
|
-
* `index` - (`true` or `false`) - Shows attribute on index page.
|
89
|
-
* `form` - (`true` or `false`) - Shows attribute on new/edit page.
|
90
|
-
* `center` - (`true` or `false`) - Adds `text-align: center` for attribute value on index page.
|
91
|
-
* `search` - (`true` or `false`) - Enables search for this attribute.
|
89
|
+
### Fields Parameters
|
92
90
|
|
93
|
-
|
91
|
+
#### index
|
92
|
+
`true` or `false`, default: `true`.
|
94
93
|
|
95
|
-
|
96
|
-
class Adminable::Blog::PostsController < Adminable::ResourcesController
|
97
|
-
set_attributes do |attributes|
|
98
|
-
# Enables search for title column
|
99
|
-
attributes.set :title, search: true
|
94
|
+
Shows field on index page.
|
100
95
|
|
101
|
-
|
102
|
-
|
96
|
+
#### form
|
97
|
+
`true` or `false`, default: `true`.
|
103
98
|
|
104
|
-
|
105
|
-
attributes.set :text, wysiwyg: true, index: false
|
99
|
+
Shows field on new/edit page.
|
106
100
|
|
107
|
-
|
108
|
-
|
101
|
+
#### center
|
102
|
+
`true` or `false`, default `true` for `integer`, `boolean`, `float` and `decimal` fields, `false` otherwise.
|
109
103
|
|
110
|
-
|
111
|
-
attributes << Adminable::Attributes::Types::String.new(:author)
|
104
|
+
Adds `text-align: center` for field value on index page.
|
112
105
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
117
|
-
```
|
106
|
+
#### search
|
107
|
+
`true` or `false`, default: `false`.
|
108
|
+
Enables search for this field.
|
118
109
|
|
119
|
-
|
110
|
+
### See Also
|
120
111
|
|
121
112
|
* Configured controller for Devise model: [app/controllers/adminable/users_controller.rb](https://github.com/droptheplot/adminable/blob/master/spec/dummy/app/controllers/adminable/users_controller.rb)
|
122
113
|
|
123
|
-
## Built-in
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
| Timestamp | + | + | | |
|
138
|
-
| Boolean | + | + | + | |
|
139
|
-
| Belongs To | | + | | |
|
140
|
-
| Has Many | | + | | |
|
114
|
+
## Built-in Fields
|
115
|
+
|
116
|
+
* String
|
117
|
+
* Text
|
118
|
+
* Integer
|
119
|
+
* Float
|
120
|
+
* Decimal
|
121
|
+
* Date
|
122
|
+
* DateTime
|
123
|
+
* Time
|
124
|
+
* Timestamp
|
125
|
+
* Boolean
|
126
|
+
* Belongs To
|
127
|
+
* Has Many
|
141
128
|
|
142
129
|
## Generating Partials
|
143
130
|
|
@@ -146,7 +133,7 @@ You can use generator to copy original partial to your application.
|
|
146
133
|
`rails g adminable:partial [layout] [type] [resource]`
|
147
134
|
|
148
135
|
* `layout` - `index` or `form`.
|
149
|
-
* `type` - `string`, `text` etc. See [Built-in
|
136
|
+
* `type` - `string`, `text` etc. See [Built-in Fields](#built-in-fields).
|
150
137
|
* `resource` - Use controller name (e.g. `users`) to replace partial only for single controller or leave blank to replace partials for all controllers.
|
151
138
|
|
152
139
|
## F.A.Q
|
@@ -9,6 +9,7 @@ module Adminable
|
|
9
9
|
end
|
10
10
|
|
11
11
|
before_action :set_entry, only: [:edit, :update, :destroy]
|
12
|
+
before_action :set_fields, only: [:index, :new, :edit, :create, :update]
|
12
13
|
|
13
14
|
before_action do
|
14
15
|
append_view_path(
|
@@ -23,7 +24,7 @@ module Adminable
|
|
23
24
|
def index
|
24
25
|
@q = @resource.model.ransack(params[:q])
|
25
26
|
@entries = Adminable::Presenters::Entries.new(
|
26
|
-
@q.result.includes(
|
27
|
+
@q.result.includes(*includes).order(id: :desc)
|
27
28
|
.page(params[:page]).per(25)
|
28
29
|
)
|
29
30
|
end
|
@@ -74,32 +75,6 @@ module Adminable
|
|
74
75
|
)
|
75
76
|
end
|
76
77
|
|
77
|
-
# Calls from children controller class to manage resource attributes
|
78
|
-
# @example Update attributes for Adminable::Blog::PostsController
|
79
|
-
# # app/controllers/adminable/blog/posts_controller.rb
|
80
|
-
#
|
81
|
-
# set_attributes do |attributes|
|
82
|
-
# # Enables search for title column
|
83
|
-
# attributes.set :title, search: true
|
84
|
-
#
|
85
|
-
# # Hides title from new and edit pages
|
86
|
-
# attributes.set :title, form: true
|
87
|
-
#
|
88
|
-
# # Adds wysiwyg plugin and hides from index table
|
89
|
-
# attributes.set :text, wysiwyg: true, index: false
|
90
|
-
#
|
91
|
-
# # Adds new attribute `password` with type `string` and some options
|
92
|
-
# attributes.add :password, :string, wysiwyg: true, index: false
|
93
|
-
#
|
94
|
-
# # Adds new attribute `author`
|
95
|
-
# attributes << Adminable::Attributes::Types::String.new(:author)
|
96
|
-
# end
|
97
|
-
def self.set_attributes
|
98
|
-
before_action do
|
99
|
-
@resource.attributes.configure { yield(@resource.attributes) }
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
78
|
private
|
104
79
|
|
105
80
|
def set_entry
|
@@ -107,14 +82,30 @@ module Adminable
|
|
107
82
|
@entry = Adminable::Presenters::Entry.new(entry)
|
108
83
|
end
|
109
84
|
|
85
|
+
def set_fields
|
86
|
+
@fields = Adminable::Presenters::Fields.new(fields)
|
87
|
+
end
|
88
|
+
|
110
89
|
def resource_model_name
|
111
90
|
controller_path.sub(%r{^adminable/}, '')
|
112
91
|
end
|
113
92
|
|
114
93
|
def resource_params
|
115
94
|
params.require(@resource.model.model_name.param_key).permit(
|
116
|
-
|
95
|
+
*fields.map(&:strong_parameter)
|
117
96
|
)
|
118
97
|
end
|
98
|
+
|
99
|
+
def fields
|
100
|
+
raise Adminable::FieldsNotDefined
|
101
|
+
end
|
102
|
+
|
103
|
+
def includes
|
104
|
+
association_fields = fields.select do |field|
|
105
|
+
%i(belongs_to has_many).include?(field.type)
|
106
|
+
end
|
107
|
+
|
108
|
+
association_fields.any? ? association_fields.map(&:name) : false
|
109
|
+
end
|
119
110
|
end
|
120
111
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
= form_for @entry, method: (@entry.persisted? ? :patch : :post) do |f|
|
2
|
-
- @
|
2
|
+
- @fields.form.each do |field|
|
3
3
|
%fieldset.form-group
|
4
|
-
= render
|
4
|
+
= render field.form_partial_path, entry: @entry, field: field
|
5
5
|
%fieldset.form-group
|
6
6
|
= f.submit t('adminable.buttons.submit'), class: 'btn btn-primary'
|
7
7
|
= link_to t('adminable.buttons.back'), polymorphic_path(@resource.model),
|
@@ -1,9 +1,9 @@
|
|
1
1
|
.card.bg-faded
|
2
2
|
.card-block
|
3
3
|
= search_form_for @q do |f|
|
4
|
-
- @
|
4
|
+
- @fields.search.each do |field|
|
5
5
|
%fieldset.form-group
|
6
|
-
= f.label
|
6
|
+
= f.label field.ransack_name, @resource.model.human_attribute_name(field.name),
|
7
7
|
class: 'text-muted'
|
8
|
-
= f.search_field
|
8
|
+
= f.search_field field.ransack_name, class: 'form-control'
|
9
9
|
= f.submit t('adminable.buttons.search'), class: 'btn btn-primary-outline'
|
@@ -1,11 +1,11 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= hidden_field_tag "#{@resource.model.model_name.param_key}[#{
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= hidden_field_tag "#{@resource.model.model_name.param_key}[#{field.key}]", nil
|
3
3
|
#clusterizeScrollArea.clusterize-scroll.associations
|
4
4
|
#clusterizeContentArea.clusterize-content
|
5
|
-
-
|
5
|
+
- Adminable::Presenters::Entries.new(entry.association(field.name).klass.all).each do |association_entry|
|
6
6
|
.association
|
7
7
|
%label.c-input.c-radio.m-a-0
|
8
|
-
= radio_button_tag "#{@resource.model.model_name.param_key}[#{
|
9
|
-
association_entry.id, entry.send(
|
8
|
+
= radio_button_tag "#{@resource.model.model_name.param_key}[#{field.key}]",
|
9
|
+
association_entry.id, entry.send(field.key) == association_entry.id
|
10
10
|
%span.c-indicator
|
11
11
|
= association_entry.link_to_self
|
@@ -1,6 +1,6 @@
|
|
1
1
|
%label.c-input.c-checkbox
|
2
|
-
= hidden_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
= check_box_tag "#{@resource.model.model_name.param_key}[#{
|
4
|
-
entry.send(
|
2
|
+
= hidden_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]", 0
|
3
|
+
= check_box_tag "#{@resource.model.model_name.param_key}[#{field.name}]", 1,
|
4
|
+
entry.send(field.key)
|
5
5
|
%span.c-indicator
|
6
|
-
= @resource.model.human_attribute_name(
|
6
|
+
= @resource.model.human_attribute_name(field.name)
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= date_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= date_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= datetime_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= datetime_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= number_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= number_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= number_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= number_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -1,12 +1,12 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= hidden_field_tag "#{@resource.model.model_name.param_key}[#{
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= hidden_field_tag "#{@resource.model.model_name.param_key}[#{field.key}][]", nil
|
3
3
|
#clusterizeScrollArea.clusterize-scroll.associations
|
4
4
|
#clusterizeContentArea.clusterize-content
|
5
|
-
-
|
5
|
+
- Adminable::Presenters::Entries.new(entry.association(field.name).klass.all).each do |association_entry|
|
6
6
|
.association
|
7
7
|
%label.c-input.c-checkbox.m-a-0
|
8
|
-
= check_box_tag "#{@resource.model.model_name.param_key}[#{
|
9
|
-
association_entry.id, entry.send(
|
8
|
+
= check_box_tag "#{@resource.model.model_name.param_key}[#{field.key}][]",
|
9
|
+
association_entry.id, entry.send(field.key).include?(association_entry.id)
|
10
10
|
%span.c-indicator
|
11
11
|
= link_to association_entry.to_name,
|
12
12
|
edit_polymorphic_path(association_entry),
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= number_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= number_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= text_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= text_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= text_area_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
4
|
-
class: "form-control #{ 'wysiwyg' if
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= text_area_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name],
|
4
|
+
class: "form-control #{ 'wysiwyg' if field.options[:wysiwyg] }", rows: 5
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= text_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= text_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -1,3 +1,3 @@
|
|
1
|
-
= render 'adminable/shared/label',
|
2
|
-
= text_field_tag "#{@resource.model.model_name.param_key}[#{
|
3
|
-
entry[
|
1
|
+
= render 'adminable/shared/label', field: field
|
2
|
+
= text_field_tag "#{@resource.model.model_name.param_key}[#{field.name}]",
|
3
|
+
entry[field.name], class: 'form-control'
|
@@ -4,30 +4,30 @@
|
|
4
4
|
= link_to t('adminable.buttons.new', resource: @resource.model.model_name.human),
|
5
5
|
new_polymorphic_path(@resource.model), class: 'btn btn-primary pull-md-right m-b-2'
|
6
6
|
.row
|
7
|
-
%div{ :class => (@
|
7
|
+
%div{ :class => (@fields.search.any? ? 'col-md-9' : 'col-md-12') }
|
8
8
|
.table-responsive.m-b-3
|
9
9
|
%table.table.table-striped
|
10
10
|
%thead
|
11
11
|
%tr
|
12
|
-
- @
|
13
|
-
%th.text-nowrap{ :class => ('text-md-center' if
|
14
|
-
= sort_link(@q,
|
12
|
+
- @fields.index.each do |field|
|
13
|
+
%th.text-nowrap{ :class => ('text-md-center' if field.options[:center]) }
|
14
|
+
= sort_link(@q, field.name, hide_indicator: true)
|
15
15
|
%th
|
16
16
|
%tbody
|
17
17
|
- @entries.each do |entry|
|
18
18
|
%tr
|
19
|
-
- @
|
20
|
-
%td{ :class => ('text-md-center' if
|
21
|
-
= render
|
22
|
-
|
19
|
+
- @fields.index.each do |field|
|
20
|
+
%td{ :class => ('text-md-center' if field.options[:center]) }
|
21
|
+
= render field.index_partial_path, entry: entry,
|
22
|
+
field: field, value: entry.public_send(field.name)
|
23
23
|
%td.text-nowrap.text-md-right
|
24
24
|
= entry.link_to_edit_small
|
25
25
|
= entry.link_to_delete_small
|
26
26
|
- if @entries.empty?
|
27
27
|
%tr
|
28
|
-
%td.text-md-center.text-muted{ :colspan => @
|
28
|
+
%td.text-md-center.text-muted{ :colspan => @fields.index.size + 1 }
|
29
29
|
No data available
|
30
|
-
- if @
|
30
|
+
- if @fields.search.any?
|
31
31
|
.col-md-3.hidden-sm-down
|
32
32
|
= render 'search'
|
33
33
|
= paginate @entries, views_prefix: 'adminable'
|
@@ -1,8 +1,8 @@
|
|
1
|
-
= label_tag
|
2
|
-
= @resource.model.human_attribute_name(
|
3
|
-
- if
|
1
|
+
= label_tag field.name do
|
2
|
+
= @resource.model.human_attribute_name(field.name)
|
3
|
+
- if field.options[:required]
|
4
4
|
%span.text-muted.m-l-1
|
5
5
|
%i.fa.fa-exclamation-circle{ 'data-toggle' => :tooltip, 'data-placement' => :right, :title => 'Required' }
|
6
|
-
- if %i(belongs_to has_many).include?(
|
6
|
+
- if %i(belongs_to has_many).include?(field.type)
|
7
7
|
%span.text-muted.m-l-1.uncheck-associations
|
8
8
|
%i.fa.fa-remove{ 'data-toggle' => :tooltip, 'data-placement' => :right, :title => 'Uncheck all' }
|
data/lib/adminable.rb
CHANGED
@@ -7,24 +7,23 @@ require 'adminable/resource'
|
|
7
7
|
require 'adminable/presenters/base'
|
8
8
|
require 'adminable/presenters/entry'
|
9
9
|
require 'adminable/presenters/entries'
|
10
|
+
require 'adminable/presenters/fields'
|
10
11
|
|
11
|
-
require 'adminable/
|
12
|
-
require 'adminable/attributes/association'
|
12
|
+
require 'adminable/field_collector'
|
13
13
|
|
14
|
-
require 'adminable/
|
15
|
-
|
16
|
-
require 'adminable/
|
17
|
-
require 'adminable/
|
18
|
-
require 'adminable/
|
19
|
-
require 'adminable/
|
20
|
-
require 'adminable/
|
21
|
-
require 'adminable/
|
22
|
-
require 'adminable/
|
23
|
-
require 'adminable/
|
24
|
-
require 'adminable/
|
25
|
-
require 'adminable/
|
26
|
-
require 'adminable/
|
27
|
-
require 'adminable/attributes/types/timestamp'
|
14
|
+
require 'adminable/fields/base'
|
15
|
+
require 'adminable/fields/belongs_to'
|
16
|
+
require 'adminable/fields/boolean'
|
17
|
+
require 'adminable/fields/date'
|
18
|
+
require 'adminable/fields/datetime'
|
19
|
+
require 'adminable/fields/decimal'
|
20
|
+
require 'adminable/fields/float'
|
21
|
+
require 'adminable/fields/has_many'
|
22
|
+
require 'adminable/fields/integer'
|
23
|
+
require 'adminable/fields/string'
|
24
|
+
require 'adminable/fields/text'
|
25
|
+
require 'adminable/fields/time'
|
26
|
+
require 'adminable/fields/timestamp'
|
28
27
|
|
29
28
|
require 'haml-rails'
|
30
29
|
require 'sass-rails'
|
data/lib/adminable/errors.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module Adminable
|
2
|
-
class
|
2
|
+
class FieldsNotDefined < StandardError
|
3
3
|
end
|
4
4
|
|
5
|
-
class
|
5
|
+
class FieldNotImplemented < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class FieldNotFound < StandardError
|
6
9
|
end
|
7
10
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Adminable
|
2
|
+
class FieldCollector
|
3
|
+
# @return [ActiveRecord::Base] activerecord model class
|
4
|
+
# @example
|
5
|
+
# Adminable::Fields::Collection.new(User).model
|
6
|
+
# # => User(id: integer, email: string, password_hash: string)
|
7
|
+
attr_reader :model
|
8
|
+
|
9
|
+
# @return [Array] fields from activerecord model
|
10
|
+
attr_reader :all
|
11
|
+
|
12
|
+
# @param model [ActiveRecord::Base] activerecord model class
|
13
|
+
def initialize(model)
|
14
|
+
@model = model
|
15
|
+
@all ||= columns + associations
|
16
|
+
end
|
17
|
+
|
18
|
+
# Collects fields from model columns
|
19
|
+
# @return [Array]
|
20
|
+
#
|
21
|
+
# rubocop:disable Metrics/MethodLength
|
22
|
+
def columns
|
23
|
+
@columns ||= [].tap do |fields|
|
24
|
+
@model.columns.reject { |a| a.name.match(/_id$/) }.each do |column|
|
25
|
+
fields << resolve(column.type, column.name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Collects fields from model associations
|
31
|
+
# @return [Array]
|
32
|
+
def associations
|
33
|
+
@associations ||= [].tap do |fields|
|
34
|
+
@model.reflect_on_all_associations.each do |association|
|
35
|
+
fields << resolve(association.macro, association.name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def resolve(type, name)
|
43
|
+
class_name = "adminable/fields/#{type}".classify
|
44
|
+
"#{class_name}.new(:#{name})"
|
45
|
+
end
|
46
|
+
|
47
|
+
def required?(name)
|
48
|
+
@model.validators_on(name).any? do |validator|
|
49
|
+
validator.class == ActiveRecord::Validations::PresenceValidator
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,44 +1,52 @@
|
|
1
1
|
module Adminable
|
2
|
-
module
|
3
|
-
# Base class for
|
2
|
+
module Fields
|
3
|
+
# Base class for fields
|
4
4
|
# @note Cannot be initialized
|
5
5
|
class Base
|
6
|
-
|
6
|
+
# @return [Symbol] field name
|
7
|
+
attr_reader :name
|
7
8
|
|
8
|
-
# @
|
9
|
+
# @return [Hash] default options for field
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
# @param name [Symbol] field name e.g. `:id` or `:title`
|
9
13
|
# @param options [Hash] options, see {default_options}
|
10
14
|
def initialize(name, options = {})
|
11
15
|
raise 'Base class cannot be initialized' if self.class == Base
|
12
16
|
|
13
17
|
@name = name.to_sym
|
14
18
|
@options = default_options.merge(options)
|
15
|
-
|
16
|
-
@association = Adminable::Attributes::Association.new(
|
17
|
-
options[:association]
|
18
|
-
) if options[:association]
|
19
19
|
end
|
20
20
|
|
21
|
+
# @return [Symbol] field form key
|
21
22
|
def key
|
22
23
|
@key ||= name
|
23
24
|
end
|
24
25
|
|
26
|
+
# @return [Symbol] controller strong parameters key
|
25
27
|
def strong_parameter
|
26
28
|
@strong_parameter ||= key
|
27
29
|
end
|
28
30
|
|
31
|
+
# @return [String] ransack form key
|
29
32
|
def ransack_name
|
30
33
|
@ransack_name ||= "#{name}_cont"
|
31
34
|
end
|
32
35
|
|
33
|
-
# @return [Symbol]
|
36
|
+
# @return [Symbol] field type
|
37
|
+
# @example
|
38
|
+
# Adminable::Fields::String.new(:title).type
|
39
|
+
# # => :string
|
34
40
|
def type
|
35
41
|
@type ||= self.class.name.demodulize.underscore.to_sym
|
36
42
|
end
|
37
43
|
|
44
|
+
# @return [String] path to field index partial
|
38
45
|
def index_partial_path
|
39
46
|
"index/#{type}"
|
40
47
|
end
|
41
48
|
|
49
|
+
# @return [String] path to field form partial
|
42
50
|
def form_partial_path
|
43
51
|
"form/#{type}"
|
44
52
|
end
|
@@ -47,12 +55,12 @@ module Adminable
|
|
47
55
|
|
48
56
|
def default_options
|
49
57
|
{
|
50
|
-
index:
|
58
|
+
index: true,
|
59
|
+
form: true,
|
60
|
+
wysiwyg: false,
|
51
61
|
search: false,
|
52
|
-
form: %i(id created_at updated_at).exclude?(name),
|
53
62
|
required: false,
|
54
|
-
center: %i(integer boolean float decimal).include?(type)
|
55
|
-
wysiwyg: false
|
63
|
+
center: %i(integer boolean float decimal).include?(type)
|
56
64
|
}
|
57
65
|
end
|
58
66
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Adminable
|
2
|
+
module Presenters
|
3
|
+
class Fields < SimpleDelegator
|
4
|
+
def index
|
5
|
+
select { |field| field.options[:index] }
|
6
|
+
end
|
7
|
+
|
8
|
+
def form
|
9
|
+
select { |field| field.options[:form] }
|
10
|
+
end
|
11
|
+
|
12
|
+
def search
|
13
|
+
select { |field| field.options[:search] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/adminable/resource.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Adminable
|
2
2
|
class Resource
|
3
|
-
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
attr_reader :name, :model, :fields
|
4
6
|
|
5
7
|
# @param name [String] resource name, usually same as the model name
|
6
8
|
def initialize(name)
|
@@ -13,22 +15,8 @@ module Adminable
|
|
13
15
|
@route ||= @model.name.underscore.pluralize.tr('/', '_')
|
14
16
|
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
@includes ||= if attributes.associations.present?
|
19
|
-
attributes.associations.map(&:name)
|
20
|
-
else
|
21
|
-
false
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [Array] collection, see {Adminable::Attributes::Collection}
|
26
|
-
def attributes
|
27
|
-
@attributes ||= Adminable::Attributes::Collection.new(@model)
|
28
|
-
end
|
29
|
-
|
30
|
-
def ==(other)
|
31
|
-
other.is_a?(Adminable::Resource) && name == other.name
|
18
|
+
def <=>(other)
|
19
|
+
other.is_a?(Adminable::Resource) && name <=> other.name
|
32
20
|
end
|
33
21
|
end
|
34
22
|
end
|
data/lib/adminable/version.rb
CHANGED
@@ -20,9 +20,17 @@ module Adminable
|
|
20
20
|
name.underscore.pluralize
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
23
|
+
def resource_class_name
|
24
24
|
"adminable/#{resource_name}".classify.pluralize
|
25
25
|
end
|
26
|
+
|
27
|
+
def model_class
|
28
|
+
name.classify.constantize
|
29
|
+
end
|
30
|
+
|
31
|
+
def fields
|
32
|
+
Adminable::FieldCollector.new(model_class).all
|
33
|
+
end
|
26
34
|
end
|
27
35
|
end
|
28
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adminable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Novikov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -370,27 +370,27 @@ files:
|
|
370
370
|
- config/locales/adminable.ru.yml
|
371
371
|
- config/routes.rb
|
372
372
|
- lib/adminable.rb
|
373
|
-
- lib/adminable/attributes/association.rb
|
374
|
-
- lib/adminable/attributes/base.rb
|
375
|
-
- lib/adminable/attributes/collection.rb
|
376
|
-
- lib/adminable/attributes/types/belongs_to.rb
|
377
|
-
- lib/adminable/attributes/types/boolean.rb
|
378
|
-
- lib/adminable/attributes/types/date.rb
|
379
|
-
- lib/adminable/attributes/types/datetime.rb
|
380
|
-
- lib/adminable/attributes/types/decimal.rb
|
381
|
-
- lib/adminable/attributes/types/float.rb
|
382
|
-
- lib/adminable/attributes/types/has_many.rb
|
383
|
-
- lib/adminable/attributes/types/integer.rb
|
384
|
-
- lib/adminable/attributes/types/string.rb
|
385
|
-
- lib/adminable/attributes/types/text.rb
|
386
|
-
- lib/adminable/attributes/types/time.rb
|
387
|
-
- lib/adminable/attributes/types/timestamp.rb
|
388
373
|
- lib/adminable/configuration.rb
|
389
374
|
- lib/adminable/engine.rb
|
390
375
|
- lib/adminable/errors.rb
|
376
|
+
- lib/adminable/field_collector.rb
|
377
|
+
- lib/adminable/fields/base.rb
|
378
|
+
- lib/adminable/fields/belongs_to.rb
|
379
|
+
- lib/adminable/fields/boolean.rb
|
380
|
+
- lib/adminable/fields/date.rb
|
381
|
+
- lib/adminable/fields/datetime.rb
|
382
|
+
- lib/adminable/fields/decimal.rb
|
383
|
+
- lib/adminable/fields/float.rb
|
384
|
+
- lib/adminable/fields/has_many.rb
|
385
|
+
- lib/adminable/fields/integer.rb
|
386
|
+
- lib/adminable/fields/string.rb
|
387
|
+
- lib/adminable/fields/text.rb
|
388
|
+
- lib/adminable/fields/time.rb
|
389
|
+
- lib/adminable/fields/timestamp.rb
|
391
390
|
- lib/adminable/presenters/base.rb
|
392
391
|
- lib/adminable/presenters/entries.rb
|
393
392
|
- lib/adminable/presenters/entry.rb
|
393
|
+
- lib/adminable/presenters/fields.rb
|
394
394
|
- lib/adminable/resource.rb
|
395
395
|
- lib/adminable/version.rb
|
396
396
|
- lib/generators/adminable/install/templates/application_controller.rb
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module Adminable
|
2
|
-
module Attributes
|
3
|
-
class Association
|
4
|
-
attr_reader :reflection, :model, :entries
|
5
|
-
|
6
|
-
# @param reflection [Object] ActiveRecord::Reflection::HasManyReflection
|
7
|
-
def initialize(reflection)
|
8
|
-
@reflection = reflection
|
9
|
-
@model = @reflection.klass
|
10
|
-
@entries = Adminable::Presenters::Entries.new(@model)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,116 +0,0 @@
|
|
1
|
-
module Adminable
|
2
|
-
module Attributes
|
3
|
-
class Collection
|
4
|
-
include Enumerable
|
5
|
-
extend Forwardable
|
6
|
-
|
7
|
-
def_delegators :@all, :[], :<<, :each, :first, :last, :push, :unshift
|
8
|
-
|
9
|
-
attr_reader :model, :all
|
10
|
-
|
11
|
-
# @param model [Class] model from Rails application e.g. `User` or `Post`
|
12
|
-
def initialize(model)
|
13
|
-
@model = model
|
14
|
-
@all ||= columns + associations
|
15
|
-
end
|
16
|
-
|
17
|
-
def index
|
18
|
-
all.select { |attribute| attribute.options[:index] }
|
19
|
-
end
|
20
|
-
|
21
|
-
def form
|
22
|
-
all.select { |attribute| attribute.options[:form] }
|
23
|
-
end
|
24
|
-
|
25
|
-
def search
|
26
|
-
all.select { |attribute| attribute.options[:search] }
|
27
|
-
end
|
28
|
-
|
29
|
-
# Collects attributes from model columns
|
30
|
-
# @return [Array]
|
31
|
-
#
|
32
|
-
# rubocop:disable Metrics/MethodLength
|
33
|
-
def columns
|
34
|
-
@columns ||= [].tap do |attributes|
|
35
|
-
@model.columns.reject { |a| a.name.match(/_id$/) }.each do |column|
|
36
|
-
begin
|
37
|
-
attributes << resolve(column.type).new(
|
38
|
-
column.name,
|
39
|
-
required: required?(column.name)
|
40
|
-
)
|
41
|
-
rescue Adminable::AttributeNotImplemented
|
42
|
-
next
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Collects attributes from model associations
|
49
|
-
# @return [Array]
|
50
|
-
def associations
|
51
|
-
@associations ||= [].tap do |attributes|
|
52
|
-
@model.reflect_on_all_associations.each do |association|
|
53
|
-
attributes << resolve(association.macro).new(
|
54
|
-
association.name,
|
55
|
-
required: required?(association.name),
|
56
|
-
association: association
|
57
|
-
)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def configure
|
63
|
-
yield
|
64
|
-
end
|
65
|
-
|
66
|
-
# Changes options for given attribute
|
67
|
-
# @param name [Symbol] name of attribute e.g. `:title`
|
68
|
-
# @param options [Hash] options to update
|
69
|
-
def set(*args)
|
70
|
-
options = args.extract_options!
|
71
|
-
names = args
|
72
|
-
|
73
|
-
options.each do |key, value|
|
74
|
-
names.each do |name|
|
75
|
-
get(name).options[key] = value
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Finds attribute by name
|
81
|
-
# @param name [Symbol] name of attribute e.g. `:title`
|
82
|
-
# @return [Object] e.g. `Adminable::Attribute::Types::String` for `:title`
|
83
|
-
def get(name)
|
84
|
-
@all.find { |attribute| attribute.name == name } ||
|
85
|
-
raise(
|
86
|
-
Adminable::AttributeNotFound,
|
87
|
-
"couldn't find attribute with name `#{name}`"
|
88
|
-
)
|
89
|
-
end
|
90
|
-
|
91
|
-
# Adds new attribute to collection
|
92
|
-
# @param name [Symbol] name of attribute e.g. `:title`
|
93
|
-
# @param type [Symbol] type of attribute e.g. `:string`
|
94
|
-
def add(name, type, options = {})
|
95
|
-
@all << resolve(type).new(name, **options)
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def resolve(type)
|
101
|
-
"adminable/attributes/types/#{type}".classify.constantize
|
102
|
-
rescue NameError
|
103
|
-
raise(
|
104
|
-
Adminable::AttributeNotImplemented,
|
105
|
-
"type `#{type}` is not supported yet."
|
106
|
-
)
|
107
|
-
end
|
108
|
-
|
109
|
-
def required?(name)
|
110
|
-
@model.validators_on(name).any? do |validator|
|
111
|
-
validator.class == ActiveRecord::Validations::PresenceValidator
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|