fae-rails 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -22
- data/config/deploy.rb +3 -3
- data/docs/customization.md +471 -0
- data/docs/helpers.md +528 -0
- data/docs/index.md +131 -0
- data/docs/javascript.md +21 -0
- data/docs/upgrading.md +28 -0
- data/docs/usage.md +627 -0
- data/lib/fae/version.rb +1 -1
- data/spec/dummy/app/views/pages/home.html.erb +1 -1
- data/vendor/assets/javascripts/chosen.jquery.min.js +2 -0
- data/vendor/assets/javascripts/grinder.js +331 -0
- data/vendor/assets/javascripts/html5shiv-printshiv.js +7 -0
- data/vendor/assets/javascripts/html5shiv.js +4 -0
- data/vendor/assets/javascripts/imagesloaded.pkgd.min.js +7 -0
- data/vendor/assets/javascripts/jquery.daterangepicker.js +1292 -0
- data/vendor/assets/javascripts/jquery.multi-select.js +532 -0
- data/vendor/assets/javascripts/jquery.simplemodal.1.4.4.js +719 -0
- data/vendor/assets/javascripts/jquery.simplemodal.1.4.4.min.js +26 -0
- data/vendor/assets/javascripts/jquery.tablesorter.js +1901 -0
- data/vendor/assets/javascripts/js.cookie.js +139 -0
- data/vendor/assets/javascripts/moment.min.js +7 -0
- data/vendor/assets/javascripts/simplemde.min.js +13 -0
- data/vendor/assets/javascripts/touch_punch.js +11 -0
- data/vendor/assets/stylesheets/chosen-sprite.png +0 -0
- data/vendor/assets/stylesheets/chosen-sprite@2x.png +0 -0
- data/vendor/assets/stylesheets/daterangepicker.css +227 -0
- data/vendor/assets/stylesheets/reset.css +48 -0
- data/vendor/assets/stylesheets/simplemde.min.css +7 -0
- metadata +28 -3
data/docs/index.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Fae Installation and Customization
|
2
|
+
|
3
|
+
* [Installation](#installation)
|
4
|
+
* [Dependencies](#dependencies)
|
5
|
+
* [Installer](#faeinstall)
|
6
|
+
* [Seeding](#db-seed)
|
7
|
+
* [Versioning](#version-management)
|
8
|
+
* [Initializer](#fae-initializer)
|
9
|
+
* [Mailer Configuration](#devise-action-mailer-configuration)
|
10
|
+
|
11
|
+
---
|
12
|
+
|
13
|
+
# Installation
|
14
|
+
|
15
|
+
Add the gem to your Gemfile and run `bundle install`
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'fae-rails'
|
19
|
+
```
|
20
|
+
Run the installer
|
21
|
+
|
22
|
+
```bash
|
23
|
+
$ rails g fae:install
|
24
|
+
```
|
25
|
+
|
26
|
+
After the installer completes, visit `/admin` and setup your first user account. That should automatically log you in to your blank Fae instance.
|
27
|
+
|
28
|
+
## Dependencies
|
29
|
+
|
30
|
+
### Rails
|
31
|
+
|
32
|
+
Fae supports Rails >= 4.1.
|
33
|
+
|
34
|
+
### Sass and sass-rails
|
35
|
+
|
36
|
+
Fae requires `sass >= 3.4` and `sass-rails >= 5`.
|
37
|
+
|
38
|
+
If you're using Rails 4.1 you'll need to update the versions in the `Gemfile`:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
gem 'sass-rails', '~> 5.0.0'
|
42
|
+
gem 'sass', '~> 3.4.0'
|
43
|
+
```
|
44
|
+
|
45
|
+
and run:
|
46
|
+
|
47
|
+
```bash
|
48
|
+
$ bundle update sass-rails
|
49
|
+
$ bundle update sass
|
50
|
+
```
|
51
|
+
|
52
|
+
## fae:install
|
53
|
+
|
54
|
+
Fae's installer will do the following:
|
55
|
+
|
56
|
+
- add Fae's namespace and route to `config/routes.rb`
|
57
|
+
- add `app/assets/stylesheets/fae.scss` for UI color management and custom CSS
|
58
|
+
- add `app/assets/javascripts/fae.js` for custom JS
|
59
|
+
- add `app/controllers/concerns/fae/nav_items.rb` to manage main navigation
|
60
|
+
- add Fae's initializer: `config/initializers/fae.rb`
|
61
|
+
- add `config/initializers/judge.rb` for validation config
|
62
|
+
- copies over migrations from Fae
|
63
|
+
- runs `rake db:migrate`
|
64
|
+
- seeds the DB with Fae defaults
|
65
|
+
|
66
|
+
## DB Seed
|
67
|
+
|
68
|
+
Fae comes with a rake task to seed the DB with defaults:
|
69
|
+
|
70
|
+
```bash
|
71
|
+
rake fae:seed_db RAILS_ENV=<your_env>
|
72
|
+
```
|
73
|
+
|
74
|
+
If you ran the installer, the task will be run automatically. But if you are setting up an established Fae instance locally or deploying to a server, running this will get you setup with some defaults.
|
75
|
+
|
76
|
+
## Version management
|
77
|
+
|
78
|
+
Fae follows semantic versioning, so you can expect the following format: `major.minor.patch`. Patch versions add bugfixes, minor versions add backwards compilable features and major versions add non-backward compatible features.
|
79
|
+
|
80
|
+
---
|
81
|
+
|
82
|
+
# Fae Initializer
|
83
|
+
|
84
|
+
Fae's default config can be overwritten in a `config/initializers/fae.rb` file.
|
85
|
+
|
86
|
+
| key | type | default | description |
|
87
|
+
| --- | ---- | ------- | ----------- |
|
88
|
+
| devise_secret_key | string | | unique Devise hash |
|
89
|
+
| devise_mailer_sender | string | change-me@example.com | address used to send Devise notifications (i.e. forgot password emails) |
|
90
|
+
| dashboard_exclusions | array | [] | array of models to hide in the dashboard |
|
91
|
+
| max_image_upload_size | integer | 2 | ceiling for image uploads in MB |
|
92
|
+
| max_file_upload_size | integer | 5 | ceiling for file uploads in MB |
|
93
|
+
| recreate_versions | boolean | false | Triggers `Fae::Image` to recreate Carrierwave versions after save. This is helpful when you have conditional versions that rely on attributes of `Fae::Image` by making sure they're saved before versions are created. |
|
94
|
+
| track_changes | boolean | true | Determines whether or not to track changes on your objects |
|
95
|
+
| tracker_history_length | integer | 15 | Determines the max number of changes logged per object |
|
96
|
+
|
97
|
+
### Example
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
Fae.setup do |config|
|
101
|
+
|
102
|
+
config.devise_secret_key = '79a3e96fecbdd893853495ff502cd387e22c9049fd30ff691115b8a0b074505be4edef6139e4be1a0a9ff407442224dbe99d94986e2abd64fd0aa01153f5be0d'
|
103
|
+
|
104
|
+
# models to exclude from dashboard list
|
105
|
+
config.dashboard_exclusions = %w( Varietal )
|
106
|
+
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
---
|
111
|
+
|
112
|
+
# Devise Action Mailer Configuration
|
113
|
+
|
114
|
+
In order for Fae's password reset email function to work you need to make sure you application can send mail and set a default url option for ActionMailer in each `config/environments/*env.rb` file.
|
115
|
+
|
116
|
+
## Example
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
Rails.application.configure do
|
120
|
+
# development.rb
|
121
|
+
config.action_mailer.default_url_options = { host: 'localhost:3000' }
|
122
|
+
# remote_development.rb
|
123
|
+
config.action_mailer.default_url_options = { host: 'dev.yoursite.com' }
|
124
|
+
# stage.rb
|
125
|
+
config.action_mailer.default_url_options = { host: 'stage.yoursite.com' }
|
126
|
+
# production.rb
|
127
|
+
config.action_mailer.default_url_options = { host: 'yoursite.com' }
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
Be sure to update this each time your domain or subdomain changes.
|
data/docs/javascript.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# JavaScript Standards
|
2
|
+
|
3
|
+
## Functions
|
4
|
+
|
5
|
+
camelCase
|
6
|
+
|
7
|
+
### Access
|
8
|
+
|
9
|
+
Methods used only within the namespace should be preceeded by an underscore. This denotes the equivalent of Ruby's `private` even those these functions are still publicly exposed.
|
10
|
+
|
11
|
+
## Variables
|
12
|
+
|
13
|
+
snake_case
|
14
|
+
|
15
|
+
### References
|
16
|
+
|
17
|
+
When referencing upper namespace, always use `_this`. `that` will not be tolerated.
|
18
|
+
|
19
|
+
### jQuery objects
|
20
|
+
|
21
|
+
Preceed all things jQuery with a `$`.
|
data/docs/upgrading.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Upgrading Fae
|
2
|
+
|
3
|
+
* [From v1.1 to v1.2](#from-v11-to-v12)
|
4
|
+
* [From v1.0 to v1.1](#from-v10-to-v11)
|
5
|
+
|
6
|
+
---
|
7
|
+
|
8
|
+
# From v1.1 to v1.2
|
9
|
+
|
10
|
+
Fae v1.2 adds a new table `Fae::Change` to track changes on your objects. After updating you'll have to copy over and run the new migrations.
|
11
|
+
|
12
|
+
```bash
|
13
|
+
$ rake fae:install:migrations
|
14
|
+
$ rake db:migrate
|
15
|
+
```
|
16
|
+
|
17
|
+
---
|
18
|
+
|
19
|
+
# From v1.0 to v1.1
|
20
|
+
|
21
|
+
[View the CHANGELOG](changelog.md#markdown-header-11)
|
22
|
+
|
23
|
+
Fae v1.1 adds a new column to `Fae::User`. After upgrading you'll have to copy over the new migration and run it.
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ rake fae:install:migrations
|
27
|
+
$ rake db:migrate
|
28
|
+
```
|
data/docs/usage.md
ADDED
@@ -0,0 +1,627 @@
|
|
1
|
+
# Usage
|
2
|
+
|
3
|
+
* [Generators](#generators)
|
4
|
+
* [Models](#models)
|
5
|
+
* [Nested Resources](#nested-resources)
|
6
|
+
* [Validation](#validation)
|
7
|
+
* [Image and File Associations](#image-and-file-associations)
|
8
|
+
* [Controllers](#controllers)
|
9
|
+
* [Navigation Items](#navigation-items)
|
10
|
+
* [Form Helpers](#form-helpers)
|
11
|
+
* [Pages and Content Blocks](#pages-and-content-blocks)
|
12
|
+
|
13
|
+
---
|
14
|
+
|
15
|
+
# Generators
|
16
|
+
|
17
|
+
Once you have Fae installed, you're ready to start generating your data model. Fae comes with a few generators that work similarly to the ones in Rails. The idea is scaffolding a model with these generators will give you a section to create, edit and delete objects.
|
18
|
+
|
19
|
+
## fae:scaffold
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
rails g fae:scaffold [ModelName] [field:type] [field:type]
|
23
|
+
```
|
24
|
+
| option | description |
|
25
|
+
|------- | ----------- |
|
26
|
+
| ModelName | singular camel-cased model name |
|
27
|
+
| field | the attributes column name |
|
28
|
+
| type | the column type (defaults to `string`), find all options in [Rails' documentaion](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-column) |
|
29
|
+
|
30
|
+
This is Fae's main generator. It will create the following:
|
31
|
+
|
32
|
+
- model
|
33
|
+
- controller and views for fully CRUDable section
|
34
|
+
- migration
|
35
|
+
- resource routes
|
36
|
+
- link in `app/controllers/concerns/fae/nav_items.rb`
|
37
|
+
|
38
|
+
### Special Attributes
|
39
|
+
|
40
|
+
**name**/**title** will automatically be set as the model's `fae_display_field`.
|
41
|
+
|
42
|
+
**position** will automatically make the section's index table sortable, be ignored from the form and add acts_as_list and default_scope to the model.
|
43
|
+
|
44
|
+
**on_prod**/**on_stage**/**active** will automatically be flag fields in the section's index and ignored in the form.
|
45
|
+
|
46
|
+
**_id**/**:references** will automatically be setup as an association in the form.
|
47
|
+
|
48
|
+
### Example
|
49
|
+
|
50
|
+
```bash
|
51
|
+
rails g fae:scaffold Person first_name last_name title body:text date_of_birth:date position:integer on_stage:boolean on_prod:boolean group:references
|
52
|
+
```
|
53
|
+
|
54
|
+
|
55
|
+
## fae:nested_scaffold
|
56
|
+
|
57
|
+
```bash
|
58
|
+
rails g fae:nested_scaffold [ModelName] [field:type] [field:type] [--parent-model=ParentModel]
|
59
|
+
```
|
60
|
+
|
61
|
+
| option | description |
|
62
|
+
| ------ | ----------- |
|
63
|
+
| `[--parent-model=ParentModel]` | an optional flag that adds the association to the generated model.|
|
64
|
+
|
65
|
+
The nested scaffold creates a model that will be nested in another object's form via the `nested_table_advanced` partial. This generator is very similar to `fae:scaffold`, the main difference is in the views that are setup to serve the nested form.
|
66
|
+
|
67
|
+
|
68
|
+
## fae:nested_index_scaffold
|
69
|
+
|
70
|
+
```bash
|
71
|
+
rails g fae:nested_index_scaffold [ModelName] [field:type] [field:type]
|
72
|
+
```
|
73
|
+
|
74
|
+
The nested index scaffold creates a normal model and a controller that supports the nested_index_form partial. This generator is very similar to `fae:scaffold`, the main difference is in the views that are setup to serve the nested form.
|
75
|
+
|
76
|
+
## fae:page
|
77
|
+
|
78
|
+
```bash
|
79
|
+
rails g fae:page [PageName] [field:type] [field:type]
|
80
|
+
```
|
81
|
+
|
82
|
+
| option | description |
|
83
|
+
|-----------|-------------|
|
84
|
+
| PageName | the name of the page |
|
85
|
+
| field | the name of the content block |
|
86
|
+
| type | the type of the content block (see table below) |
|
87
|
+
|
88
|
+
| content block | associated object |
|
89
|
+
|---------------|-------------------|
|
90
|
+
| string | Fae::TextField |
|
91
|
+
| text | Fae::TextArea |
|
92
|
+
| image | Fae::Image |
|
93
|
+
| file | Fae::File |
|
94
|
+
|
95
|
+
The page generator scaffolds a page into Fae's content blocks system. More on that later, for now here's what it does:
|
96
|
+
|
97
|
+
- creates or adds to `app/controllers/admin/content_blocks_controller.rb`
|
98
|
+
- creates a `#{page_name}_page.rb` model
|
99
|
+
- creates a form view in `app/views/admin/content_blocks/#{page_name}.html.slim`
|
100
|
+
|
101
|
+
### Example
|
102
|
+
|
103
|
+
```bash
|
104
|
+
rails g fae:page AboutUs title:string introduction:text body:text header_image:image
|
105
|
+
```
|
106
|
+
|
107
|
+
---
|
108
|
+
|
109
|
+
# Models
|
110
|
+
|
111
|
+
A generated model will start off with sensible defaults based on the attributes you used in the generator. Here are some common custom additions you should be aware of.
|
112
|
+
|
113
|
+
## Fae's Base Model Concern
|
114
|
+
|
115
|
+
To allow Fae to push out any model specific updates to your application models, include the concern at the top of the class body:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
class Release < ActiveRecord::Base
|
119
|
+
include Fae::BaseModelConcern
|
120
|
+
# ...
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
## fae_display_field
|
125
|
+
|
126
|
+
Fae uses `fae_display_field` in a our table views. Defining it as a class method that returns the value of one or multiple attributes is required for those tables to display properly.
|
127
|
+
|
128
|
+
If the model is generated, then it will use `name` or `title` by default.
|
129
|
+
|
130
|
+
### Examples
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
def fae_display_field
|
134
|
+
title
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
def fae_display_field
|
140
|
+
"#{last_name}, #{first_name}"
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
## for_fae_index
|
145
|
+
|
146
|
+
Fae uses a class method called `for_fae_index` as a scope for index views and associated content in form elements. This method is inherited from `Fae::BaseModelConcern`.
|
147
|
+
|
148
|
+
By default, this method uses position, name, or title attributes. If it can't find any of those it will raise the following exception:
|
149
|
+
|
150
|
+
```
|
151
|
+
No order_method found, please define for_fae_index as a #{model_name} class method to set a custom scope.
|
152
|
+
```
|
153
|
+
|
154
|
+
To override the default or get rid of this exception, simple define the class method in your model:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
def self.for_fae_index
|
158
|
+
order(:first_name)
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
## to_csv
|
163
|
+
|
164
|
+
Fae uses a class method called `to_csv` as a method to export all the objects related to a given model to a csv. This method is inherited from `Fae::BaseModelConcern`. It is meant to be called from the index action.
|
165
|
+
|
166
|
+
|
167
|
+
## Nested Resources
|
168
|
+
|
169
|
+
If you use nested resource routes and want updates on those objects to show up in the dashboard, you'll need to define it's parent for Fae to know how to link them.
|
170
|
+
|
171
|
+
To do this, add a class method called `fae_parent` pointing to the underscored association to the parent object. Here is an example:
|
172
|
+
|
173
|
+
routes.rb
|
174
|
+
```ruby
|
175
|
+
namespace :admin do
|
176
|
+
resources :groups do
|
177
|
+
resources :people
|
178
|
+
end
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
models/person.rb
|
183
|
+
```ruby
|
184
|
+
# if this is the parent
|
185
|
+
belongs_to :group
|
186
|
+
|
187
|
+
# then you'll define this
|
188
|
+
def fae_parent
|
189
|
+
group
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
## Validation
|
194
|
+
|
195
|
+
Fae doesn't deal with any validation definitions in your application models, you'll have to add those. However, there are some pre-defined regex validation helpers to use in your models. See examples below.
|
196
|
+
|
197
|
+
### Validation Helpers
|
198
|
+
|
199
|
+
Fae validation helpers come in two flavors; regex only, and complete hash.
|
200
|
+
|
201
|
+
Regex:
|
202
|
+
|
203
|
+
| option | description |
|
204
|
+
|---------------|---------------------------------------------------------|
|
205
|
+
| slug_regex | no spaces or special characters |
|
206
|
+
| email_regex | valid email with @ and . |
|
207
|
+
| url_regex | http and https urls |
|
208
|
+
| zip_regex | 5 digit zip code |
|
209
|
+
| youtube_regex | matches youtube id, i.e. the 11 digits after "watch?v=" |
|
210
|
+
|
211
|
+
example:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
validates :slug,
|
215
|
+
uniqueness: true,
|
216
|
+
presence: true,
|
217
|
+
format: {
|
218
|
+
with: Fae.validation_helpers.slug_regex,
|
219
|
+
message: 'no spaces or special characters'
|
220
|
+
}
|
221
|
+
```
|
222
|
+
|
223
|
+
Complete:
|
224
|
+
|
225
|
+
| option | description |
|
226
|
+
|--------------|--------------------------------------------------|
|
227
|
+
| slug | uniqueness, presence, regex format with message |
|
228
|
+
| email | regex format with message, allow blank |
|
229
|
+
| url | regex form with message, allow blank |
|
230
|
+
| zip | regex format with message, allow blank |
|
231
|
+
| youtube_url | regex format with message, allow blank |
|
232
|
+
|
233
|
+
example:
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
validates :slug, Fae.validation_helpers.slug
|
237
|
+
```
|
238
|
+
|
239
|
+
### Judge and Uniqueness
|
240
|
+
|
241
|
+
Fae uses [Judge](https://github.com/joecorcoran/judge) to automatically add client side validation from the declarations in the models. The caveat is Judge requires you to expose any attributes that have a uniqueness validation. You can do this in `config/initializers/jugde.rb`:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
Judge.configure do
|
245
|
+
expose Person, :slug
|
246
|
+
expose Wine, :name, :slug
|
247
|
+
end
|
248
|
+
```
|
249
|
+
|
250
|
+
## Image and File Associations
|
251
|
+
|
252
|
+
Fae provides models for images and files: `Fae::Image` and `Fae::File` respectively. These models come with their own attributes, validations and uploaders and can be polymorphically associated to your application models.
|
253
|
+
|
254
|
+
Here's a basic example:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
has_one :bottle_shot, -> { where(attached_as: 'bottle_shot') },
|
258
|
+
as: :imageable,
|
259
|
+
class_name: '::Fae::Image',
|
260
|
+
dependent: :destroy
|
261
|
+
accepts_nested_attributes_for :bottle_shot, allow_destroy: true
|
262
|
+
```
|
263
|
+
|
264
|
+
Here's the breakdown:
|
265
|
+
|
266
|
+
`has_one :bottle_shot` sets the name of the custom association.
|
267
|
+
|
268
|
+
`-> { where(attached_as: 'bottle_shot') }` sets the scope of the association. If we have more than one `Fae::Image` we need to set the `attached_as` to distinguish it from other images associated to that model.
|
269
|
+
|
270
|
+
`as: :imageable, class_name: '::Fae::Image'` defines the polymorphic association.
|
271
|
+
|
272
|
+
`dependent: :destroy` will make sure the image object is destroyed along with the parent object.
|
273
|
+
|
274
|
+
`accepts_nested_attributes_for :bottle_shot, allow_destroy: true` allows the image/file uploader to be nested in the parent object's form in Fae.
|
275
|
+
|
276
|
+
### Other Examples
|
277
|
+
|
278
|
+
An onject with many gallery images:
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
has_many :gallery_images, -> { where(attached_as: 'gallery_images') },
|
282
|
+
as: :imageable,
|
283
|
+
class_name: '::Fae::Image',
|
284
|
+
dependent: :destroy
|
285
|
+
accepts_nested_attributes_for :gallery_images, allow_destroy: true
|
286
|
+
```
|
287
|
+
|
288
|
+
A file example:
|
289
|
+
|
290
|
+
```ruby
|
291
|
+
has_one :tasting_notes_pdf, -> { where(attached_as: 'tasting_notes_pdf') },
|
292
|
+
as: :fileable,
|
293
|
+
class_name: '::Fae::File',
|
294
|
+
dependent: :destroy
|
295
|
+
accepts_nested_attributes_for :tasting_notes_pdf, allow_destroy: true
|
296
|
+
```
|
297
|
+
|
298
|
+
If the object only has one image association, you can get away with omitting the scope:
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
has_one :image, as: :imageable, class_name: '::Fae::Image', dependent: :destroy
|
302
|
+
accepts_nested_attributes_for :image, allow_destroy: true
|
303
|
+
```
|
304
|
+
|
305
|
+
---
|
306
|
+
|
307
|
+
# Controllers
|
308
|
+
|
309
|
+
Controllers that manage models in Fae should be namespaced and inherit from `Fae::BaseController`. Controllers that are generated have this already in place:
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
module Admin
|
313
|
+
class PeopleController < Fae::BaseController
|
314
|
+
# ...
|
315
|
+
end
|
316
|
+
end
|
317
|
+
```
|
318
|
+
For a standard Fae section you can pretty much leave your controller empty. Most of the magic happens in [Fae::BaseController](../app/controllers/fae/base_controller.rb). But there are a few things you should know about.
|
319
|
+
|
320
|
+
## Building Assets
|
321
|
+
|
322
|
+
If the section manages objects with associated images or files, you'll need to build those objects by overriding the `build_assets` private method.
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
module Admin
|
326
|
+
class WinesController < Fae::BaseController
|
327
|
+
|
328
|
+
private
|
329
|
+
|
330
|
+
def build_assets
|
331
|
+
@item.build_bottle_shot if @item.bottle_shot.blank?
|
332
|
+
@item.build_label_pdf if @item.label_pdf.blank?
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
```
|
337
|
+
|
338
|
+
## Custom Titles in Views
|
339
|
+
|
340
|
+
If you'd like to change the generated titles in the Fae views, you can do so with the following `before_action`.
|
341
|
+
|
342
|
+
```ruby
|
343
|
+
module Admin
|
344
|
+
class WinesController < Fae::BaseController
|
345
|
+
before_action do
|
346
|
+
set_class_variables 'Vinos'
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
```
|
351
|
+
|
352
|
+
This will affect the add button text and index/form page titles.
|
353
|
+
|
354
|
+
---
|
355
|
+
|
356
|
+
# Navigation Items
|
357
|
+
|
358
|
+
When you use the generators, a link to the section appears in the main navigation of the admin. This is done by automatically adding to `app/controllers/concerns/fae/nav_items.rb`. However, this file is available for you to customize the nav however you'd like.
|
359
|
+
|
360
|
+
The navigation is built of of the array set in the `nav_items` method. Each array item is a hash with these available keys:
|
361
|
+
|
362
|
+
| Key | Type | Description |
|
363
|
+
| --- | ---- | ----------- |
|
364
|
+
| text | string | the link's text |
|
365
|
+
| path | string or named route | the link's href (defaults to '#') |
|
366
|
+
| class | string | an added class to the link |
|
367
|
+
| sublinks | array of hashes | nested links to be displayed in a dropdown |
|
368
|
+
|
369
|
+
## Named Routes in nav_items.rb
|
370
|
+
|
371
|
+
Since the `nav_items` concern hooks directly into Fae, named routes need context using the following prefixes:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
def nav_items
|
375
|
+
[
|
376
|
+
# use `main_app.` for application routes (even in your admin)
|
377
|
+
{ text: 'Cities', path: main_app.admin_cities_path },
|
378
|
+
# use `fae.` for Fae routes
|
379
|
+
{ text: 'Pages', path: fae.pages_path }
|
380
|
+
]
|
381
|
+
end
|
382
|
+
```
|
383
|
+
|
384
|
+
## Sublinks
|
385
|
+
|
386
|
+
When sublinks are present, the main nav item will trigger a drawer holding the sublinks to open/close. Add sublinks using the following format:
|
387
|
+
|
388
|
+
```ruby
|
389
|
+
def nav_items
|
390
|
+
[
|
391
|
+
{
|
392
|
+
text: 'Items with sublinks', sublinks: [
|
393
|
+
{ text: 'Item Sublink 1', path: main_app.admin_some_path },
|
394
|
+
{ text: 'Item Sublink 2', path: main_app.admin_someother_path }
|
395
|
+
]
|
396
|
+
}
|
397
|
+
]
|
398
|
+
end
|
399
|
+
```
|
400
|
+
|
401
|
+
## Dynamic Content in Nav
|
402
|
+
|
403
|
+
Dynamic content is allowed in the the `nav_items` concern. Here's an example:
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
module Fae
|
407
|
+
module NavItems
|
408
|
+
extend ActiveSupport::Concern
|
409
|
+
|
410
|
+
def nav_items
|
411
|
+
[
|
412
|
+
{ text: 'Releases', path: main_app.admin_releases_path },
|
413
|
+
{ text: 'Tiers', sublinks: tier_sublinks }
|
414
|
+
]
|
415
|
+
end
|
416
|
+
|
417
|
+
private
|
418
|
+
|
419
|
+
def tier_sublinks
|
420
|
+
tiers_arr = [{ text: 'New Tier', path: main_app.new_admin_tier_path }]
|
421
|
+
Tier.each do |tier|
|
422
|
+
tiers_arr << { text: tier.name, path: main_app.edit_admin_tier_path(tier) }
|
423
|
+
end
|
424
|
+
tiers_arr
|
425
|
+
end
|
426
|
+
|
427
|
+
end
|
428
|
+
end
|
429
|
+
```
|
430
|
+
|
431
|
+
---
|
432
|
+
|
433
|
+
# Form Helpers
|
434
|
+
[Click here for full documentation on Fae's form helpers](helpers.md#markdown-header-form-helpers)
|
435
|
+
|
436
|
+
Generated forms start you off on a good place to manage the object's content, but chances are you'll want to customize them and add more fields as you data model evolves. Fae provides a number of form helpers to help you leverage Fae's built in features will allowing customization when needed.
|
437
|
+
|
438
|
+
Form helpers in Fae use the [simple_form](https://github.com/plataformatec/simple_form) gem as it's base. In most cases options that simple_form accepts can be passed into these helpers directly. The reason why we've established these helpers it to allow for customized options. They also provide a method to directly hook into Fae, so we can push out features and bugfixes.
|
439
|
+
|
440
|
+
---
|
441
|
+
|
442
|
+
# View Helpers and Partials
|
443
|
+
|
444
|
+
Fae also provides a number of other built in view helpers and partials, that are documented in [helpers.md](helpers.md).
|
445
|
+
|
446
|
+
[Click here for view helpers](helpers.md#markdown-header-view-helpers)
|
447
|
+
[Click here for Fae partials](helpers.md#markdown-header-fae-partials)
|
448
|
+
|
449
|
+
---
|
450
|
+
|
451
|
+
# Custom Helpers
|
452
|
+
|
453
|
+
If you want to add your own helper methods for your Fae views, simply create and add them to `app/helpers/fae/fae_helper.rb`.
|
454
|
+
|
455
|
+
```ruby
|
456
|
+
module Fae
|
457
|
+
module FaeHelper
|
458
|
+
# ...
|
459
|
+
end
|
460
|
+
end
|
461
|
+
```
|
462
|
+
|
463
|
+
---
|
464
|
+
|
465
|
+
# Cloning
|
466
|
+
|
467
|
+
[Click here for full documentation on Fae's cloning](cloning.md)
|
468
|
+
|
469
|
+
Many users find it easier to clone records that have similar content, rather than spending the time manually setting them up. Fae has the ability to allow one-click clones from the index or the edit form, as well as flexibility to whitelist attributes and clone assocations.
|
470
|
+
|
471
|
+
---
|
472
|
+
|
473
|
+
# Pages and Content Blocks
|
474
|
+
|
475
|
+
Fae has a built in system to handle content blocks that are statically wired to pages in your site. This is for content that isn't tied to an object in your data model, e.g. home, about and terms content.
|
476
|
+
|
477
|
+
The system is just your basic inherited singleton with dynamic polymorphic associations. Kidding aside, the complexity of the system is hidden and "it just works™" if you use the generators and/or follow the conventions. This allows for dynamic content blocks that can be added without database migrations and wired up without static IDs!
|
478
|
+
|
479
|
+
## Pages vs Content Blocks
|
480
|
+
|
481
|
+
**Pages** are groups of **content blocks** based on the actual pages they appear on the site. For the following example, we will use a page called `AboutUs`, which will have content blocks for `hero_image`, `title`, `introduction`, `body` and `annual_report`.
|
482
|
+
|
483
|
+
## Generating Pages
|
484
|
+
|
485
|
+
It is highly recommended you use the built in generator to add pages, especially if it's the first page in the admin. Let's do that for our example:
|
486
|
+
|
487
|
+
```bash
|
488
|
+
rails g fae:page AboutUs hero_image:image hero_text:string introduction:text body:text annual_report:file
|
489
|
+
```
|
490
|
+
|
491
|
+
This will generate...
|
492
|
+
|
493
|
+
`app/models/about_us_page.rb`
|
494
|
+
```ruby
|
495
|
+
class AboutUsPage < Fae::StaticPage
|
496
|
+
|
497
|
+
@slug = 'about_us'
|
498
|
+
|
499
|
+
# required to set the has_one associations, Fae::StaticPage will build these associations dynamically
|
500
|
+
def self.fae_fields
|
501
|
+
{
|
502
|
+
hero_image: { type: Fae::Image },
|
503
|
+
hero_text: { type: Fae::TextField },
|
504
|
+
introduction: { type: Fae::TextArea },
|
505
|
+
body: { type: Fae::TextArea },
|
506
|
+
annual_report: { type: Fae::File }
|
507
|
+
}
|
508
|
+
end
|
509
|
+
|
510
|
+
end
|
511
|
+
```
|
512
|
+
|
513
|
+
`app/views/fae/pages/about_us.html.slim`
|
514
|
+
```ruby
|
515
|
+
= simple_form_for @item, url: fae.update_content_block_path(slug: @item.slug), method: :put do |f|
|
516
|
+
section.main_content-header
|
517
|
+
.main_content-header-wrapper
|
518
|
+
= render 'fae/shared/form_header', header: @item
|
519
|
+
= render 'fae/shared/form_buttons', f: f
|
520
|
+
|
521
|
+
.main_content-sections
|
522
|
+
section.main_content-section
|
523
|
+
.main_content-section-area
|
524
|
+
= fae_input f, :title
|
525
|
+
|
526
|
+
= fae_image_form f, :hero_image
|
527
|
+
= fae_content_form f, :hero_text
|
528
|
+
= fae_content_form f, :introduction
|
529
|
+
= fae_content_form f, :body
|
530
|
+
= fae_file_form f, :annual_report
|
531
|
+
```
|
532
|
+
|
533
|
+
Since this is the first page the generator will create `app/controllers/admin/content_blocks_controller.rb`, otherwise it would just add to the `fae_pages` array.
|
534
|
+
|
535
|
+
|
536
|
+
```ruby
|
537
|
+
module Admin
|
538
|
+
class ContentBlocksController < Fae::StaticPagesController
|
539
|
+
|
540
|
+
private
|
541
|
+
|
542
|
+
def fae_pages
|
543
|
+
[AboutUsPage]
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
```
|
548
|
+
|
549
|
+
## Adding Content Blocks
|
550
|
+
|
551
|
+
Chances are you'll need to add content blocks to a page after it's been generated. To do so simply:
|
552
|
+
|
553
|
+
- add the new content blocks to `fae_fields` in the `AboutUsPage` model
|
554
|
+
- add the appropriate form elements to the form at `about_us.html.slim`
|
555
|
+
- `fae_content_form` for `Fae::TextField` and `Fae::TextArea`
|
556
|
+
- `fae_image_form` for `Fae::Image`
|
557
|
+
- `fae_file_form` for `Fae::File`
|
558
|
+
|
559
|
+
## Getting Your Content Blocks
|
560
|
+
|
561
|
+
Each page generated is a singleton model and each content block is an association to a Fae model.
|
562
|
+
|
563
|
+
To get an instance of your page:
|
564
|
+
|
565
|
+
```ruby
|
566
|
+
@about_us_page = AboutUsPage.instance
|
567
|
+
```
|
568
|
+
|
569
|
+
Then to get content from a `Fae::TextField` and `Fae::TextArea`:
|
570
|
+
|
571
|
+
```ruby
|
572
|
+
# for `Fae::TextField` or `Fae::TextArea`
|
573
|
+
@about_us_page.hero_text.content
|
574
|
+
# ... or ...
|
575
|
+
@about_us_page.hero_text_content
|
576
|
+
|
577
|
+
# for `Fae::Image` or `Fae::File`
|
578
|
+
@about_us_page.hero_image.asset.url
|
579
|
+
# for `Fae::Image` only
|
580
|
+
@about_us_page.hero_image.asset.alt
|
581
|
+
@about_us_page.hero_image.asset.caption
|
582
|
+
```
|
583
|
+
|
584
|
+
## Invalid Content Block Names
|
585
|
+
|
586
|
+
Content blocks are just associations on the page model, which inherits from `Fae::Page`. Because of this, attributes on `Fae::Page` are invalid names for content blocks. These attributes are:
|
587
|
+
|
588
|
+
- title
|
589
|
+
- slug
|
590
|
+
- position
|
591
|
+
- on_prod
|
592
|
+
- on_stage
|
593
|
+
- created_at
|
594
|
+
- updated_at
|
595
|
+
|
596
|
+
## Validations on Content Blocks
|
597
|
+
|
598
|
+
Since content blocks are setup as associations, adding validations to them can be tricky. To make it easier we setup a method directly in `fae_fields` hash that will dynamically add the validations to the appropriate model and apply the `data-validate` attribute in the form so Judge can do it's best to validate the content on the frontend.
|
599
|
+
|
600
|
+
To add validations to a content block, add a validates option with your rules on a specific content block in `fae_fields`. Format the rules just as you would normal model validations.
|
601
|
+
|
602
|
+
`app/models/about_us_page.rb`
|
603
|
+
```ruby
|
604
|
+
def self.fae_fields
|
605
|
+
{
|
606
|
+
hero_image: { type: Fae::Image },
|
607
|
+
hero_text: {
|
608
|
+
type: Fae::TextField,
|
609
|
+
validates: { presence: true }
|
610
|
+
},
|
611
|
+
introduction: {
|
612
|
+
type: Fae::TextArea,
|
613
|
+
validates: {
|
614
|
+
presence: true,
|
615
|
+
length: {
|
616
|
+
maximum: 100,
|
617
|
+
message: 'should be brief (100 characters or less)'
|
618
|
+
}
|
619
|
+
},
|
620
|
+
},
|
621
|
+
body: { type: Fae::TextArea },
|
622
|
+
annual_report: { type: Fae::File }
|
623
|
+
}
|
624
|
+
end
|
625
|
+
```
|
626
|
+
|
627
|
+
Validations can only be applied to types `Fae::TextField` and `Fae::TextArea`.
|