adminable 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|