adminable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +94 -33
  3. data/app/assets/javascripts/adminable/scripts.js +8 -5
  4. data/app/assets/stylesheets/adminable/application.scss +15 -0
  5. data/app/controllers/adminable/application_controller.rb +0 -3
  6. data/app/controllers/adminable/resources_controller.rb +49 -24
  7. data/app/models/concerns/adminable/resource_concern.rb +15 -0
  8. data/app/views/adminable/resources/_form.html.haml +5 -9
  9. data/app/views/adminable/resources/_search.html.haml +2 -2
  10. data/app/views/adminable/resources/edit.html.haml +1 -1
  11. data/app/views/adminable/resources/form/_belongs_to.html.haml +13 -3
  12. data/app/views/adminable/resources/form/_boolean.html.haml +5 -2
  13. data/app/views/adminable/resources/form/_date.html.haml +3 -0
  14. data/app/views/adminable/resources/form/_datetime.html.haml +3 -2
  15. data/app/views/adminable/resources/form/_decimal.html.haml +3 -2
  16. data/app/views/adminable/resources/form/_float.html.haml +3 -2
  17. data/app/views/adminable/resources/form/_has_many.html.haml +12 -8
  18. data/app/views/adminable/resources/form/_integer.html.haml +3 -2
  19. data/app/views/adminable/resources/form/_string.html.haml +3 -2
  20. data/app/views/adminable/resources/form/_text.html.haml +4 -2
  21. data/app/views/adminable/resources/form/_time.html.haml +3 -0
  22. data/app/views/adminable/resources/form/_timestamp.html.haml +3 -0
  23. data/app/views/adminable/resources/index.html.haml +12 -14
  24. data/app/views/adminable/resources/index/_belongs_to.html.haml +4 -5
  25. data/app/views/adminable/resources/index/_date.html.haml +2 -0
  26. data/app/views/adminable/resources/index/_has_many.html.haml +1 -2
  27. data/app/views/adminable/resources/index/_text.html.haml +1 -1
  28. data/app/views/adminable/resources/index/_time.html.haml +2 -0
  29. data/app/views/adminable/resources/index/_timestamp.html.haml +2 -0
  30. data/app/views/adminable/shared/_label.html.haml +6 -3
  31. data/app/views/layouts/adminable/application.html.haml +3 -4
  32. data/config/locales/adminable.en.yml +4 -1
  33. data/config/locales/adminable.ru.yml +21 -0
  34. data/config/routes.rb +1 -1
  35. data/lib/adminable.rb +9 -4
  36. data/lib/adminable/attributes/association.rb +7 -14
  37. data/lib/adminable/attributes/base.rb +36 -18
  38. data/lib/adminable/attributes/collection.rb +75 -36
  39. data/lib/adminable/attributes/types/belongs_to.rb +1 -3
  40. data/lib/adminable/attributes/types/date.rb +8 -0
  41. data/lib/adminable/attributes/types/has_many.rb +0 -6
  42. data/lib/adminable/attributes/types/time.rb +8 -0
  43. data/lib/adminable/attributes/types/timestamp.rb +8 -0
  44. data/lib/adminable/configuration.rb +7 -2
  45. data/lib/adminable/engine.rb +5 -1
  46. data/lib/adminable/errors.rb +7 -0
  47. data/lib/adminable/presenters/base_presenter.rb +11 -0
  48. data/lib/adminable/presenters/entries_presenter.rb +55 -0
  49. data/lib/adminable/presenters/entry_presenter.rb +68 -0
  50. data/lib/adminable/resource.rb +9 -15
  51. data/lib/adminable/version.rb +1 -1
  52. data/lib/generators/adminable/install/templates/application_controller.rb +2 -0
  53. data/lib/generators/adminable/install_generator.rb +27 -0
  54. data/lib/generators/{cms → adminable}/resource/templates/resource_controller.rb.erb +0 -0
  55. data/lib/generators/{cms → adminable}/resource_generator.rb +0 -0
  56. metadata +77 -33
  57. data/lib/adminable/extensions/devise.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8c8c3f78e6cda87d730a35355df1313ab3fd7674
4
- data.tar.gz: 95a90979dce955f6408263afeb5b163a8e0104e5
3
+ metadata.gz: f528b224feece004bb0e267e9d3c7c59fc8cb82c
4
+ data.tar.gz: 1cef3dc442b2ac04713f0695762429afdf0b4637
5
5
  SHA512:
6
- metadata.gz: 7fd364eb2250b70f05ff858899bbb8fe836d338d2c568808211d3f1c4390ace87d04b7f7a3eaea610f96d1d9a8b0b8ba5ebdd1112fcef180cb205f0f181ac2f9
7
- data.tar.gz: e6b5dd483ccd126c981c3e0656fb8a02fff620937d1279f395400a6bd17267f7ea0c84d2f89a4e64d59c64561a16c33844bc331a1fa39a9795695f329ea9f5d8
6
+ metadata.gz: a463389478cd68106b2e48567d1efe341f6aff36f087b5c0067e5d84d42c825ca020bae79b6650c3158b4433f00767582c5c2357435fb717c77f2dd33e7b6276
7
+ data.tar.gz: de6a89ee40356740af5537167d0411cca5c922323339ff38a2d6be3c55b3264e2f3ab70e35194f1a48e3694db248aa32585e59df74a459bcd74f212616926d1e
data/README.md CHANGED
@@ -1,15 +1,28 @@
1
1
  # Adminable
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/adminable.svg)](https://badge.fury.io/rb/adminable)
4
+ [![Build Status](https://travis-ci.org/droptheplot/adminable.svg?branch=master)](https://travis-ci.org/droptheplot/adminable)
5
+ [![Code Climate](https://codeclimate.com/github/droptheplot/adminable/badges/gpa.svg)](https://codeclimate.com/github/droptheplot/adminable)
6
+ [![Test Coverage](https://codeclimate.com/github/droptheplot/adminable/badges/coverage.svg)](https://codeclimate.com/github/droptheplot/adminable/coverage)
7
+ [![Dependency Status](https://gemnasium.com/badges/github.com/droptheplot/adminable.svg)](https://gemnasium.com/github.com/droptheplot/adminable)
8
+
9
+
3
10
  Simple admin interface for Ruby on Rails applications.
4
11
 
5
12
  ## Features
6
13
 
7
- * Built with common Rails controllers without DSL.
14
+ * Built with common Rails controllers with a small DSL.
8
15
  * Supports namespaced models.
9
- * Has simple search with Ransack.
10
- * Uses Bootstrap 4.0.
16
+ * Has simple search with [Ransack](https://github.com/activerecord-hackery/ransack).
17
+ * Uses [Bootstrap](https://github.com/twbs/bootstrap) 4.0.
18
+ * Handles a lot of associations with [Clusterize.js](https://github.com/NeXTs/Clusterize.js).
19
+ * Has built-in WYSIWYG editor [TinyMCE](https://github.com/tinymce/tinymce).
11
20
  * Mobile friendly.
12
21
 
22
+ ![Example1](https://raw.githubusercontent.com/droptheplot/adminable/master/screenshots/1.png)
23
+
24
+ ![Example2](https://raw.githubusercontent.com/droptheplot/adminable/master/screenshots/2.png)
25
+
13
26
  ## Installation
14
27
 
15
28
  Add this line to your application's Gemfile:
@@ -28,55 +41,103 @@ Or install it yourself as:
28
41
  $ gem install adminable
29
42
  ```
30
43
 
31
- ## Built-in Attributes
44
+ ## Getting Started
32
45
 
33
- List of attributes with default modifiable parameters.
46
+ First things first. Add routes and create `application_controller.rb` class using generator:
34
47
 
35
- ##### String
48
+ ```bash
49
+ rails g adminable:install
50
+ # => create app/controllers/adminable/application_controller.rb
51
+ # => insert config/routes.rb
52
+ ```
36
53
 
37
- * show: `true`
38
- * center: `false`
54
+ #### Generating Resources
39
55
 
40
- ##### Text
56
+ For example you have model `User`, then run:
41
57
 
42
- * show: `true`
43
- * center: `false`
44
- * wysiwyg: `true`
58
+ ```bash
59
+ rails g adminable:resource user
60
+ # => create app/controllers/adminable/users_controller.rb
61
+ ```
45
62
 
46
- ##### Integer
63
+ For namespaced models, like `Blog::Post`, use:
47
64
 
48
- * show: `true`
49
- * center: `true`
65
+ ```bash
66
+ rails g adminable:resource blog/post
67
+ # => create app/controllers/adminable/blog/posts_controller.rb
68
+ ```
50
69
 
51
- ##### Float
70
+ #### Customizing Attributes
52
71
 
53
- * show: `true`
54
- * center: `true`
72
+ You can update attributes with simple DSL inside `set_attributes` block:
55
73
 
56
- ##### Decimal
74
+ ##### For existing attributes
57
75
 
58
- * show: `true`
59
- * center: `true`
76
+ ```ruby
77
+ set(name, options = {})
78
+ ```
60
79
 
61
- ##### DateTime
80
+ ##### For new attributes
62
81
 
63
- * show: `true`
64
- * center: `false`
82
+ ```ruby
83
+ add(name, type, options = {})
84
+ ```
85
+
86
+ ##### Attributes Parameters
87
+
88
+ * `index` - *(true/false)* - Shows attribute on index page.
89
+ * `form` - *(true/false)* - Shows attribute on new/edit page.
90
+ * `center` - *(true/false)* - Adds `text-align: center` for attribute value on index page.
91
+ * `search` - *(true/false)* - Enables search for this attribute.
92
+
93
+ ##### Examples
94
+
95
+ ```ruby
96
+ class Adminable::Blog::PostsController < Adminable::ResourcesController
97
+ set_attributes do |attributes|
98
+ # Enables search for title column
99
+ attributes.set :title, search: true
100
+
101
+ # Hides title from new and edit pages
102
+ attributes.set :title, form: true
103
+
104
+ # Adds wysiwyg plugin and hides from index table
105
+ attributes.set :text, wysiwyg: true, index: false
65
106
 
66
- ##### Boolean
107
+ # Adds new attribute `password` with type `string` and some options
108
+ attributes.add :password, :string, wysiwyg: true, index: false
67
109
 
68
- * show: `true`
69
- * center: `true`
110
+ # Adds new attribute `author`
111
+ attributes << Adminable::Attributes::Types::String.new(:author)
70
112
 
71
- ##### Belongs To
113
+ # Allows search for multiple attributes
114
+ attributes.set :title, :body, search: true
115
+ end
116
+ end
117
+ ```
118
+
119
+ ##### See Also
72
120
 
73
- * show: `true`
74
- * center: `false`
121
+ * 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)
75
122
 
76
- ##### Has Many
123
+ ## Built-in Attributes
77
124
 
78
- * show: `true`
79
- * center: `false`
125
+ List of attributes with default parameters.
126
+
127
+ | | index | form | center | wysiwyg |
128
+ |------------|-------|------|--------|---------|
129
+ | String | + | + | | |
130
+ | Text | + | + | | + |
131
+ | Integer | + | + | + | |
132
+ | Float | + | + | + | |
133
+ | Decimal | + | + | + | |
134
+ | Date | + | + | | |
135
+ | DateTime | + | + | | |
136
+ | Time | + | + | | |
137
+ | Timestamp | + | + | | |
138
+ | Boolean | + | + | + | |
139
+ | Belongs To | | + | | |
140
+ | Has Many | | + | | |
80
141
 
81
142
  ## Contributing
82
143
 
@@ -4,12 +4,13 @@ $(document).ready(function() {
4
4
  scrollId: 'clusterizeScrollArea',
5
5
  contentId: 'clusterizeContentArea'
6
6
  });
7
- };
7
+ }
8
8
 
9
- $('.toggleCheckbox').click(function() {
10
- var checkbox = $(this).children('input[type=checkbox]');
11
- checkbox.prop('checked', !checkbox.prop('checked'));
12
- $(this).toggleClass('active');
9
+ $('.uncheck-associations').click(function() {
10
+ var inputs = $(this).parent().next().next('.associations').find('input[type=radio], input[type=checkbox]');
11
+ inputs.each(function(index, value) {
12
+ value.checked = false;
13
+ });
13
14
  });
14
15
 
15
16
  tinymce.init({
@@ -17,4 +18,6 @@ $(document).ready(function() {
17
18
  menubar: false,
18
19
  statusbar: false
19
20
  });
21
+
22
+ $('[data-toggle="tooltip"]').tooltip();
20
23
  });
@@ -1,3 +1,18 @@
1
1
  $font-size-root: 14px !default;
2
2
 
3
3
  @import 'bootstrap';
4
+
5
+ .associations {
6
+ border: 1px solid $input-border-color;
7
+ border-radius: $border-radius;
8
+ .association {
9
+ padding: .75rem;
10
+ &:not(:last-child) {
11
+ border-bottom: 1px solid $gray-lighter;
12
+ }
13
+ }
14
+ }
15
+
16
+ .uncheck-associations {
17
+ cursor: pointer;
18
+ }
@@ -1,7 +1,4 @@
1
1
  module Adminable
2
2
  class ApplicationController < ActionController::Base
3
- def welcome
4
- redirect_to polymorphic_path(Adminable::Configuration.resources.first.route)
5
- end
6
3
  end
7
4
  end
@@ -1,17 +1,28 @@
1
1
  module Adminable
2
2
  class ResourcesController < ApplicationController
3
- before_action :set_resource
3
+ def initialize(*)
4
+ @resource = Adminable::Configuration.find_resource(resource_model)
5
+
6
+ super
7
+ end
8
+
4
9
  before_action :set_entry, only: [:edit, :update, :destroy]
5
10
 
6
11
  before_action do
7
- append_view_path Adminable::Engine.root.join('app/views/adminable', controller_name)
8
- append_view_path Adminable::Engine.root.join('app/views/adminable/resources')
12
+ append_view_path(
13
+ [
14
+ Adminable::Engine.root.join('app/views/adminable', controller_name),
15
+ Adminable::Engine.root.join('app/views/adminable/resources')
16
+ ]
17
+ )
9
18
  end
10
19
 
11
20
  def index
12
21
  @q = @resource.model.ransack(params[:q])
13
- @entries = @q.result.includes(*@resource.includes).all
14
- .page(params[:page]).per(25)
22
+ @entries = Adminable::EntriesPresenter.new(
23
+ @q.result.includes(*@resource.includes).order(id: :desc)
24
+ .page(params[:page]).per(25)
25
+ )
15
26
  end
16
27
 
17
28
  def new
@@ -21,6 +32,7 @@ module Adminable
21
32
  def edit
22
33
  end
23
34
 
35
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
24
36
  def create
25
37
  @entry = @resource.model.new(resource_params)
26
38
 
@@ -31,7 +43,7 @@ module Adminable
31
43
  resource: @resource.model.model_name.human
32
44
  )
33
45
  else
34
- flash.now[:alert] = @entry.errors.full_messages
46
+ flash.now[:alert] = @entry.errors.full_messages.first
35
47
  render :new
36
48
  end
37
49
  end
@@ -44,7 +56,7 @@ module Adminable
44
56
  resource: @resource.model.model_name.human
45
57
  )
46
58
  else
47
- flash.now[:alert] = @entry.errors.full_messages
59
+ flash.now[:alert] = @entry.errors.full_messages.first
48
60
  render :edit
49
61
  end
50
62
  end
@@ -59,25 +71,38 @@ module Adminable
59
71
  )
60
72
  end
61
73
 
62
- private
63
-
64
- def set_resource
65
- @resource = Adminable::Configuration.find_resource(resource_model).clone
66
-
67
- @resource.attributes.index = index_attributes
68
- @resource.attributes.form = form_attributes
69
- end
70
-
71
- def set_entry
72
- @entry = @resource.model.find(params[:id])
74
+ # Calls from children controller class to manage resource attributes
75
+ # @example Update attributes for Adminable::Blog::PostsController
76
+ # # app/controllers/adminable/blog/posts_controller.rb
77
+ #
78
+ # set_attributes do |attributes|
79
+ # # Enables search for title column
80
+ # attributes.set :title, search: true
81
+ #
82
+ # # Hides title from new and edit pages
83
+ # attributes.set :title, form: true
84
+ #
85
+ # # Adds wysiwyg plugin and hides from index table
86
+ # attributes.set :text, wysiwyg: true, index: false
87
+ #
88
+ # # Adds new attribute `password` with type `string` and some options
89
+ # attributes.add :password, :string, wysiwyg: true, index: false
90
+ #
91
+ # # Adds new attribute `author`
92
+ # attributes << Adminable::Attributes::Types::String.new(:author)
93
+ # end
94
+ def self.set_attributes
95
+ before_action do
96
+ @resource.attributes.configure { yield(@resource.attributes) }
73
97
  end
98
+ end
74
99
 
75
- def index_attributes
76
- @resource.attributes.index
77
- end
100
+ private
78
101
 
79
- def form_attributes
80
- @resource.attributes.form
102
+ def set_entry
103
+ @entry = Adminable::EntryPresenter.new(
104
+ @resource.model.find(params[:id])
105
+ )
81
106
  end
82
107
 
83
108
  def resource_model
@@ -86,7 +111,7 @@ module Adminable
86
111
 
87
112
  def resource_params
88
113
  params.require(@resource.model.model_name.param_key).permit(
89
- *@resource.attributes.form.values.map(&:strong_parameter)
114
+ *@resource.attributes.form.map(&:strong_parameter)
90
115
  )
91
116
  end
92
117
  end
@@ -0,0 +1,15 @@
1
+ module Adminable
2
+ module ResourceConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ def adminable
6
+ %i(title name email login id).each do |name|
7
+ begin
8
+ return OpenStruct.new(name: public_send(name))
9
+ rescue NoMethodError
10
+ next
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,14 +1,10 @@
1
- = form_for @entry do |f|
2
- - @resource.attributes.form.each_value do |attribute|
3
- - if attribute.show?
4
- %fieldset.form-group
5
- = render attribute.form_partial_path, f: f, entry: @entry,
6
- attribute: attribute
1
+ = form_for @entry, method: (@entry.persisted? ? :patch : :post) do |f|
2
+ - @resource.attributes.form.each do |attribute|
3
+ %fieldset.form-group
4
+ = render attribute.form_partial_path, entry: @entry, attribute: attribute
7
5
  %fieldset.form-group
8
6
  = f.submit t('adminable.buttons.submit'), class: 'btn btn-primary'
9
7
  = link_to t('adminable.buttons.back'), polymorphic_path(@resource.model),
10
8
  class: 'btn btn-secondary-outline'
11
9
  - if @entry.persisted?
12
- = link_to t('adminable.buttons.delete'), polymorphic_path(@entry),
13
- class: 'btn btn-danger-outline pull-xs-right', method: :delete,
14
- data: { confirm: t('adminable.ui.confirm') }
10
+ = @entry.link_to_delete
@@ -1,9 +1,9 @@
1
1
  .card.bg-faded
2
2
  .card-block
3
3
  = search_form_for @q do |f|
4
- - @resource.attributes.ransack.each_value do |attribute|
4
+ - @resource.attributes.search.each do |attribute|
5
5
  %fieldset.form-group
6
6
  = f.label attribute.ransack_name, @resource.model.human_attribute_name(attribute.name),
7
7
  class: 'text-muted'
8
8
  = f.search_field attribute.ransack_name, class: 'form-control'
9
- = f.submit class: 'btn btn-primary-outline'
9
+ = f.submit t('adminable.buttons.search'), class: 'btn btn-primary-outline'
@@ -1,5 +1,5 @@
1
1
  .row
2
2
  .col-md-8.col-md-offset-2
3
3
  %h1.m-b-3
4
- = t('adminable.titles.edit', resource: @resource.model.model_name.human)
4
+ = t('adminable.titles.edit', resource: @resource.model.model_name.human, id: @entry.id)
5
5
  = render 'form'
@@ -1,3 +1,13 @@
1
- = render 'adminable/shared/label', f: f, attribute: attribute
2
- = f.select attribute.key, attribute.options_for_select(entry),
3
- { include_blank: I18n.t('adminable.ui.not_selected') }, { class: 'form-control c-select' }
1
+ = render 'adminable/shared/label', attribute: attribute
2
+ = hidden_field_tag "#{@resource.model.model_name.param_key}[#{attribute.key}]", nil
3
+ #clusterizeScrollArea.clusterize-scroll.associations
4
+ #clusterizeContentArea.clusterize-content
5
+ - attribute.association.all.each do |association_entry|
6
+ .association
7
+ %label.c-input.c-radio.m-a-0
8
+ = radio_button_tag "#{@resource.model.model_name.param_key}[#{attribute.key}]",
9
+ association_entry.id, entry.send(attribute.key) == association_entry.id
10
+ %span.c-indicator
11
+ = link_to association_entry.to_name,
12
+ edit_polymorphic_path(association_entry),
13
+ target: '_blank'
@@ -1,3 +1,6 @@
1
- = f.label attribute.name do
2
- = f.check_box attribute.name
1
+ %label.c-input.c-checkbox
2
+ = hidden_field_tag "#{@resource.model.model_name.param_key}[#{attribute.name}]", 0
3
+ = check_box_tag "#{@resource.model.model_name.param_key}[#{attribute.name}]", 1,
4
+ entry.send(attribute.key)
5
+ %span.c-indicator
3
6
  = @resource.model.human_attribute_name(attribute.name)
@@ -0,0 +1,3 @@
1
+ = render 'adminable/shared/label', attribute: attribute
2
+ = date_field_tag "#{@resource.model.model_name.param_key}[#{attribute.name}]",
3
+ entry[attribute.name], class: 'form-control'
@@ -1,2 +1,3 @@
1
- = render 'adminable/shared/label', f: f, attribute: attribute
2
- = f.datetime_field attribute.name, class: 'form-control'
1
+ = render 'adminable/shared/label', attribute: attribute
2
+ = datetime_field_tag "#{@resource.model.model_name.param_key}[#{attribute.name}]",
3
+ entry[attribute.name], class: 'form-control'