adminable 0.0.1 → 0.0.2

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.
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'