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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e4e2e9807100f19e49edaaee29e6a7d9888f7f9
|
4
|
+
data.tar.gz: c357c40edd30cbff37e17efdf919c578c4565c4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb295b46e0bc983487229d8f7105314c94c5975215fbf2188dff4792ac7710deb97882eed162882d71539af24e82e50e861aebf5a058354ab03a438fc8c0b560
|
7
|
+
data.tar.gz: 80617c4bbca334d9f02dda18410772c30b9d3c3a91bca061736d78f4b5c95c39eb01c21d7c1ce80ece0cd311f6f27db825d008a03bf314d33610bd55f0dd4d53
|
data/README.md
CHANGED
@@ -1,34 +1,25 @@
|
|
1
1
|
# Meet Fae
|
2
2
|
|
3
|
-
|
3
|
+
![Shippable Status](https://img.shields.io/shippable/5522ec125ab6cc1352b9bb77.svg)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/wearefine/fae/badges/gpa.svg)](https://codeclimate.com/github/wearefine/fae)
|
5
|
+
|
6
|
+
Fae is CMS for Rails unlike any other. Like most Rails CMS engines, Fae provides all the basics to get you up and running: authentication, a responsive UI, form element and workflows. But unlike other CMS engines, Fae's methodology is based around generators and a DSL over configuration. This allows you to get to a working CMS very quickly, but gives you the flexibility to customize any piece you need.
|
4
7
|
|
5
8
|
Fae requires Rails >= 4.1.
|
6
9
|
|
7
10
|
## Documentation
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
### Upgrading Guide
|
18
|
-
|
19
|
-
https://bitbucket.org/wearefine/fae/src/master/docs/upgrading.md
|
20
|
-
|
21
|
-
### Changelog
|
22
|
-
|
23
|
-
https://bitbucket.org/wearefine/fae/src/master/CHANGELOG.md
|
24
|
-
|
25
|
-
### Contibuting and Maintenance
|
26
|
-
|
27
|
-
https://bitbucket.org/wearefine/fae/src/master/CONTRIBUTING.md
|
12
|
+
* [Installation](docs/index.md)
|
13
|
+
* [Usage](docs/usage.md)
|
14
|
+
* [Customization](docs/customization.md)
|
15
|
+
* [Form and View Helpers](docs/helpers.md)
|
16
|
+
* [Upgrading Guide](docs/upgrading.md)
|
17
|
+
* [Changelog](CHANGELOG.md)
|
18
|
+
* [Contributing and Maintenance](CONTRIBUTING.md)
|
28
19
|
|
29
|
-
|
20
|
+
## License
|
30
21
|
|
31
|
-
|
22
|
+
[MIT](LICENSE)
|
32
23
|
|
33
24
|
|
34
25
|
|
data/config/deploy.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
set :application, '
|
2
|
-
set :repo_url, 'git@
|
1
|
+
set :application, 'Fae Dummy'
|
2
|
+
set :repo_url, 'git@github.com:wearefine/fae.git'
|
3
3
|
|
4
4
|
set :scm, :git
|
5
5
|
|
@@ -17,7 +17,7 @@ set :keep_releases, 5
|
|
17
17
|
# variable for forked version of capistrano-rails
|
18
18
|
set :rails_path, 'spec/dummy'
|
19
19
|
|
20
|
-
server '
|
20
|
+
server '54.164.40.189',
|
21
21
|
user: 'fae',
|
22
22
|
roles: %w{web app db},
|
23
23
|
port: 8022,
|
@@ -0,0 +1,471 @@
|
|
1
|
+
# Customization
|
2
|
+
|
3
|
+
* [Overview](#overview)
|
4
|
+
* [Multiple Language Support](#multiple-language-support)
|
5
|
+
* [Filtering](#filtering)
|
6
|
+
* [Change Tracker](#change-tracker)
|
7
|
+
* [CSS Classes](#css-classes)
|
8
|
+
* [Cloning](#cloning)
|
9
|
+
|
10
|
+
---
|
11
|
+
|
12
|
+
# Overview
|
13
|
+
|
14
|
+
Fae is meant to allow some radically customization. You can stray completely from Fae's generators and helpers and build your own admin section. However, if you stray from Fae standards you lose the benefit of future bug fixes and feature Fae may provide.
|
15
|
+
|
16
|
+
If you need to create custom classes, it's recommended you inherit from a Fae class and if you need to update a Fae class, look for a concern to inject into first.
|
17
|
+
|
18
|
+
## Fae Model Concerns
|
19
|
+
|
20
|
+
Each one of Fae's models has a built in concern. You can create that concern in your application to easily inject logic into built in models, following Rails' concern pattern. E.g. adding methods to `app/models/concerns/fae/role_concern.rb` will make them accessible to `Fae::Role`.
|
21
|
+
|
22
|
+
### Example: Adding OAuth2 logic to Fae::User
|
23
|
+
|
24
|
+
Say we wanted to add a lookup class method to `Fae::User` to allow for Google OAuth2 authentication. We simply need to add the following to our application:
|
25
|
+
|
26
|
+
`app/models/concerns/fae/user_concern.rb`
|
27
|
+
```ruby
|
28
|
+
module Fae
|
29
|
+
module UserConcern
|
30
|
+
extend ActiveSupport::Concern
|
31
|
+
|
32
|
+
included do
|
33
|
+
# overidde Fae::User devise settings
|
34
|
+
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:google_oauth2]
|
35
|
+
end
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
# add new class method to Fae::User
|
39
|
+
def self.find_for_google_oauth2(access_token, signed_in_resource = nil)
|
40
|
+
data = access_token.info
|
41
|
+
user = Fae::User.find_by_email(data['email'])
|
42
|
+
|
43
|
+
unless user
|
44
|
+
user = Fae::User.create(
|
45
|
+
first_name: data['name'],
|
46
|
+
email: data['email'],
|
47
|
+
role_id: 3,
|
48
|
+
active: 1,
|
49
|
+
password: Devise.friendly_token[0, 20]
|
50
|
+
)
|
51
|
+
end
|
52
|
+
user
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
### Available Fae Concerns
|
61
|
+
|
62
|
+
| Fae Class | Concern Path |
|
63
|
+
|----------------------------|--------------|
|
64
|
+
| Fae::ApplicationController | app/controllers/concerns/application_controller_concern.rb |
|
65
|
+
| Fae::File | app/models/concerns/file_concern.rb |
|
66
|
+
| Fae::Image | app/models/concerns/image_concern.rb |
|
67
|
+
| Fae::Option | app/models/concerns/option_concern.rb |
|
68
|
+
| Fae::StaticPage | app/models/concerns/static_page_concern.rb |
|
69
|
+
| Fae::TextArea | app/models/concerns/text_area_concern.rb |
|
70
|
+
| Fae::TextField | app/models/concerns/text_field_concern.rb |
|
71
|
+
| Fae::User | app/models/concerns/user_concern.rb |
|
72
|
+
|
73
|
+
|
74
|
+
## Overriding Classes
|
75
|
+
|
76
|
+
If there's no way to inherit from or inject into a Fae class, your last effort would be to override it. To do that, simply copy the Fae class into your application in the same path found in Fae and customize it from there.
|
77
|
+
|
78
|
+
E.g. if you need to customize Fae's `image_controller.rb`, copy the file from Fae into your application at `app/controllers/fae/image_controller.rb`.
|
79
|
+
|
80
|
+
## Overriding Uploaders
|
81
|
+
|
82
|
+
If you need to override the uplodaers `Fae::Image` and `Fae::File` use, you can use the method in the previous section. To customize the `Fae::ImageUploader` just copy file to `app/uploaders/fae/image_uploader.rb` and make your updates.
|
83
|
+
|
84
|
+
This is handy when you need to update the `extension_white_list` or set your own resizing logic.
|
85
|
+
|
86
|
+
## Custom JS/CSS
|
87
|
+
|
88
|
+
Fae creates two files in your assets pipeline that allow custom JS and CSS in your admin.
|
89
|
+
|
90
|
+
### fae.js
|
91
|
+
|
92
|
+
`app/assets/javascripts/fae.js` compiles into `fae/application.js`. As noted in the file, you can use it as a mainfest (to add a lot of JS) or to simply add JS directly to.
|
93
|
+
|
94
|
+
#### Example: fae.js as a Manifest File
|
95
|
+
|
96
|
+
```JavaScript
|
97
|
+
// This file is compiled into fae/application.js
|
98
|
+
// use this as another manifest file if you have a lot of javascript to add
|
99
|
+
// or just add your javascript directly to this file
|
100
|
+
//
|
101
|
+
//= require admin/plugins
|
102
|
+
//= require admin/main
|
103
|
+
```
|
104
|
+
|
105
|
+
### fae.scss
|
106
|
+
|
107
|
+
`app/assets/stylesheets/fae.scss` compiles into `fae/application.css`. Styles added to this files will be declared before other Fae styles. This file also provide a SCSS variable for Fae's highlight color: `$c-custom-highlight` (which defaults to #31a7e6).
|
108
|
+
|
109
|
+
```CSS
|
110
|
+
// Do Not Delete this page! FAE depends on it in order to set its highlight color.
|
111
|
+
// $c-custom-highlight: #000;
|
112
|
+
```
|
113
|
+
|
114
|
+
## Overriding The Landing Page
|
115
|
+
|
116
|
+
If you want to ignore the default dashboard and make one of your views the landing page you can add a redirect route in your Fae namespace.
|
117
|
+
|
118
|
+
`config/routes.rb`
|
119
|
+
```ruby
|
120
|
+
# ...
|
121
|
+
namespace :admin do
|
122
|
+
get '/', to: redirect('/admin/people')
|
123
|
+
# ...
|
124
|
+
```
|
125
|
+
|
126
|
+
---
|
127
|
+
|
128
|
+
# Multiple Language Support
|
129
|
+
|
130
|
+
Fae support a language nav that makes managing content in multiple languages easy. The language nav will display all available languages. Clicking a specific language will only display fields specific to that language.
|
131
|
+
|
132
|
+
## Configure
|
133
|
+
|
134
|
+
To setup the language nav first define all languages Fae will be managing content in.
|
135
|
+
|
136
|
+
`config/initializers/fae.rb`
|
137
|
+
```ruby
|
138
|
+
config.languages = {
|
139
|
+
en: 'English',
|
140
|
+
zh: 'Chinese',
|
141
|
+
ja: 'Japanese'
|
142
|
+
}
|
143
|
+
```
|
144
|
+
|
145
|
+
The convention of this hash is important as the keys with have to match the database column suffixes of the specific language fields. The values will be used as the link text in the language nav.
|
146
|
+
|
147
|
+
## Database Column Naming
|
148
|
+
|
149
|
+
As mentioned above, the column names of fields supporting multiple languages will have to follow this convention:
|
150
|
+
|
151
|
+
```
|
152
|
+
"#{attribute_name}_#{language_abbreviation}`
|
153
|
+
```
|
154
|
+
|
155
|
+
E.g. the english version of the title attribute would be `title_en`.
|
156
|
+
|
157
|
+
Using Fae's generators let's quickly scaffold a model that supports multiple languages (columns without suffixes will be treated normally:
|
158
|
+
|
159
|
+
```bash
|
160
|
+
$ rails g fae:scaffold Person name title_en title_zh title_ja intro_en:text intro_zh:text intro_ja:text
|
161
|
+
```
|
162
|
+
|
163
|
+
## Language Nav Partial
|
164
|
+
|
165
|
+
Then finally, you'll need to add the `fae/shared/language_nav` partial to the form, as the first child of `section.main_content-header`:
|
166
|
+
|
167
|
+
`app/views/admin/people/_form.html.slim`
|
168
|
+
```slim
|
169
|
+
= simple_form_for(['admin', @item]) do |f|
|
170
|
+
section.main_content-header
|
171
|
+
|
172
|
+
== render 'fae/shared/language_nav'
|
173
|
+
|
174
|
+
.main_content-header-wrapper
|
175
|
+
// ...
|
176
|
+
```
|
177
|
+
|
178
|
+
---
|
179
|
+
|
180
|
+
# Filtering
|
181
|
+
|
182
|
+
If you need to filter your content on your table views, Fae provides a system and helpers to do so.
|
183
|
+
|
184
|
+
Using the helpers provided, the filter form will POST to a filter action inherited from `Fae::BaseController`. You can override this action, but by default it will pass the params to a class method in your model called `filter`. It's then up you to scope the data that gets returned and rendered in the table.
|
185
|
+
|
186
|
+
Let's walk through an example. Using the `Person` model from above, let's say a person `belongs_to :company` and `has_many :groups`. We'll want to use select filters for companies and groups, and a keyword search to filter by people and company name.
|
187
|
+
|
188
|
+
## Route
|
189
|
+
|
190
|
+
First, we'll need to add `post 'filter', on: :collection` to our `people` resources:
|
191
|
+
|
192
|
+
`config/routes.rb`
|
193
|
+
```ruby
|
194
|
+
resources :people do
|
195
|
+
post 'filter', on: :collection
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
## View Helpers
|
200
|
+
|
201
|
+
Next we'll add the form to our view as the first child of `.main_content-section-area`:
|
202
|
+
|
203
|
+
`app/views/admin/people/index.html.slim`
|
204
|
+
```slim
|
205
|
+
// ...
|
206
|
+
.main_content-section-area
|
207
|
+
|
208
|
+
== fae_filter_form do
|
209
|
+
== fae_filter_select :company
|
210
|
+
== fae_filter_select :groups
|
211
|
+
|
212
|
+
table.index_table.main_table-sort_columns
|
213
|
+
// ...
|
214
|
+
```
|
215
|
+
|
216
|
+
The search field is built into `fae_filter_form`, but we'll need to provide a `fae_filter_select` for each select element in our filter bar.
|
217
|
+
|
218
|
+
Full documentation on both helpers can be found in the [helpers.md](helpers.md).
|
219
|
+
|
220
|
+
## Class Methods
|
221
|
+
|
222
|
+
Finally we need to define our class methods to scope the `Person` class. This data will be assigned to `@items` and injected into the table via AJAX.
|
223
|
+
|
224
|
+
### filter(params)
|
225
|
+
|
226
|
+
`ModelName#filter(params)` will be the scope when data is filtered. The `params` passed in will be the data directly from the `fae_filter_select` helpers we defined, plus `params['search']` from the seach field.
|
227
|
+
|
228
|
+
From the form above we can assume our params look like this:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
{
|
232
|
+
'search' => 'text from search field',
|
233
|
+
'company' => 12, # value from company select
|
234
|
+
'groups' => 3 # value from groups select
|
235
|
+
}
|
236
|
+
```
|
237
|
+
|
238
|
+
So let's use that data to craft our class method.
|
239
|
+
|
240
|
+
`app/models/person.rb`
|
241
|
+
```ruby
|
242
|
+
def self.filter(params)
|
243
|
+
# build conditions if specific params are present
|
244
|
+
conditions = {}
|
245
|
+
conditions[:company_id] = params['company'] if params['company'].present?
|
246
|
+
conditions['groups.id'] = params['groups'] if params['groups'].present?
|
247
|
+
|
248
|
+
# use good 'ol MySQL to seach if search param is present
|
249
|
+
search = []
|
250
|
+
if params['search'].present?
|
251
|
+
search = ["people.name LIKE ? OR companies.name LIKE ?", "%#{params['search']}%", "%#{params['search']}%"]
|
252
|
+
end
|
253
|
+
|
254
|
+
# apply conditions and search from above to our scope
|
255
|
+
order(:name)
|
256
|
+
.includes(:company, :groups).references(:company, :groups)
|
257
|
+
.where(conditions).where(search)
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
### filter_all
|
262
|
+
|
263
|
+
There's also a `ModelName#filter_all` which is called when you reset the filter form. This defaults to the `for_fae_index` scope, but you can override it if you need to.
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
def self.filter_all
|
267
|
+
where.not(name: 'John').order(:position)
|
268
|
+
end
|
269
|
+
```
|
270
|
+
|
271
|
+
---
|
272
|
+
|
273
|
+
# Change Tracker
|
274
|
+
|
275
|
+
Fae has a build in system to track the changes of the records in your admin. By default it's on, tracking the last 15 times a record has been changed. Make sure any model you want to track has `include Fae::BaseModelConcern` at the top.
|
276
|
+
|
277
|
+
For each change the tracker tracks what kind of change it is (create, update or delete), what attributes were changed, who changed it and when it happened.
|
278
|
+
|
279
|
+
## Global Options
|
280
|
+
|
281
|
+
You can turn off tracking altogether or update how many revisions the tracker keeps with the following options set in `config/initializers/fae.rb`.
|
282
|
+
|
283
|
+
| key | type | default | description |
|
284
|
+
| --- | ---- | ------- | ----------- |
|
285
|
+
| track_changes | boolean | true | Determines whether or not to track changes on your objects
|
286
|
+
| tracker_history_length | integer | 15 | Determines the max number of changes logged per object
|
287
|
+
|
288
|
+
### Example
|
289
|
+
|
290
|
+
`config/initializers/fae.rb`
|
291
|
+
```ruby
|
292
|
+
Fae.setup do |config|
|
293
|
+
|
294
|
+
config.tracker_history_length = 10
|
295
|
+
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
## Blacklisting Models and Attributes
|
300
|
+
|
301
|
+
If you want to turn off tracking on specific attibutes or a model altogether you can define an optional instance method `fae_tracker_blacklist`.
|
302
|
+
|
303
|
+
### Blacklisting a Model
|
304
|
+
|
305
|
+
To blacklist an entire model have `fae_tracker_blacklist` return 'all'.
|
306
|
+
|
307
|
+
```ruby
|
308
|
+
class DontTrackMe < ActiveRecord::Base
|
309
|
+
include Fae::BaseModelConcern
|
310
|
+
|
311
|
+
def fae_tracker_blacklist
|
312
|
+
'all'
|
313
|
+
end
|
314
|
+
end
|
315
|
+
```
|
316
|
+
|
317
|
+
### Blacklisting Attributes
|
318
|
+
|
319
|
+
To blacklist specific attributes have 'fae_tracker_blacklist' return an array of attribute names as symbols or strings.
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
class DontTrackMe < ActiveRecord::Base
|
323
|
+
include Fae::BaseModelConcern
|
324
|
+
|
325
|
+
def fae_tracker_blacklist
|
326
|
+
[:position, :slug]
|
327
|
+
end
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
## Accessing the Tracked Changes
|
332
|
+
|
333
|
+
Each model that includes `Fae::BaseModelConcern` will have the following association:
|
334
|
+
|
335
|
+
```
|
336
|
+
has_many :tracked_changes
|
337
|
+
```
|
338
|
+
|
339
|
+
Each tracked change is a record of `Fae::Change` and has the following attrubtes available
|
340
|
+
|
341
|
+
| attribute | description |
|
342
|
+
| --------- | ----------- |
|
343
|
+
| `changeable` | a polymorphic association back to the changed record |
|
344
|
+
| `user` | an association to the user that updated the record |
|
345
|
+
| `change_type` | how the record was changed, options are: created, updated or deleted |
|
346
|
+
| `updated_attributes` | an array of attributes changed (for updated records only) |
|
347
|
+
| `updated_at` | when the change occured |
|
348
|
+
|
349
|
+
### Example Usage
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
@item.tracked_changes.each do |change|
|
353
|
+
"This item was #{change.change_type} by #{change.user.first_name} on {change.updated_at}."
|
354
|
+
end
|
355
|
+
```
|
356
|
+
|
357
|
+
## Display Tracked Changes Table
|
358
|
+
|
359
|
+
Fae provides a partial to display tracked changes in an object's form. Read more about `render 'fae/shared/recent_changes'` here:
|
360
|
+
|
361
|
+
[helpers.md#markdown-header-recent_changes](helpers.md#markdown-header-recent_changes)
|
362
|
+
|
363
|
+
---
|
364
|
+
|
365
|
+
# CSS Classes
|
366
|
+
|
367
|
+
## Fixed position table headers
|
368
|
+
|
369
|
+
For extra long tables, add the class `sticky-table-header` to a `table` and scroll away. Multiple sticky tables can appear on one page.
|
370
|
+
|
371
|
+
## Collapsible tables
|
372
|
+
|
373
|
+
Some pages have multiple tables that are easier to navigate if tables can be shown or hidden. Wrap each table in a `.collapsible` div and prepend an `h3` with the item's name and count. Example below and on the dummy app's `events/index` page.
|
374
|
+
|
375
|
+
```slim
|
376
|
+
.main_content-section-area
|
377
|
+
.collapsible
|
378
|
+
h3 All Wine (#{@all_wine.length})
|
379
|
+
table.index_table
|
380
|
+
....
|
381
|
+
.collapsible
|
382
|
+
h3 White & Sparkling Wine (#{@white_sparkling_wine.length})
|
383
|
+
table.index_table
|
384
|
+
....
|
385
|
+
```
|
386
|
+
|
387
|
+
For best results, include an Open/Close All toggle.
|
388
|
+
|
389
|
+
```slim
|
390
|
+
.main_content-section-area
|
391
|
+
.collapsible-toggle Open All
|
392
|
+
.collapsible
|
393
|
+
....
|
394
|
+
```
|
395
|
+
|
396
|
+
---
|
397
|
+
|
398
|
+
# Cloning
|
399
|
+
|
400
|
+
So you want to easily, automagically clone a record and its children? Lucky for you, we made it easy to do that! You're welcome.
|
401
|
+
|
402
|
+
## Basic
|
403
|
+
|
404
|
+
The most basic implementation of this feature clones the record, all it's attributes (except id, created and updated at) and any belongs_to associations via foreign_keys. We also check for any uniqueness validators on your attributes and rename them to "attribute-#", starting at 2, for your convenience.
|
405
|
+
|
406
|
+
## Add Buttons
|
407
|
+
|
408
|
+
You may add the clone button to the index, edit form, or both.
|
409
|
+
|
410
|
+
**Examples**
|
411
|
+
|
412
|
+
#### For Index
|
413
|
+
|
414
|
+
Add the following to your `thead`, usually after 'Delete':
|
415
|
+
|
416
|
+
```slim
|
417
|
+
th class="main_table-header-clone" data-sorter="false" Clone
|
418
|
+
```
|
419
|
+
|
420
|
+
And to your `tbody`:
|
421
|
+
|
422
|
+
```slim
|
423
|
+
td = fae_clone_button item
|
424
|
+
```
|
425
|
+
|
426
|
+
#### For Form
|
427
|
+
|
428
|
+
Simply pass `cloneable: true` into your form_buttons partial. You may also edit the default text 'Clone', by passing in `clone_button_text` and your own string.
|
429
|
+
|
430
|
+
```ruby
|
431
|
+
render 'fae/shared/form_buttons', cloneable: true, clone_button_text: 'Duplicate Me!'
|
432
|
+
```
|
433
|
+
|
434
|
+
That's all for basic set-up.
|
435
|
+
|
436
|
+
## Advanced
|
437
|
+
|
438
|
+
If you want complete control over which attributes and associations are cloned, we wouldn't call you a control freak. We've baked in some nice simple methods to make this possible.
|
439
|
+
|
440
|
+
**Note:** Asset cloning is not currently supported, so if you try to pass in those associations, cloning will fail.
|
441
|
+
|
442
|
+
## Whitelisting Attributes
|
443
|
+
|
444
|
+
If you want to whitelist attributes to be cloned, you may add the `attributes_for_cloning` method into your controller's private method. Just pass in an array of symbols and we will take care to only copy those attributes over.
|
445
|
+
|
446
|
+
**Note:** please make sure to include _everything_ that is required, or the record will fail to get created. You've been warned.
|
447
|
+
|
448
|
+
**Example**
|
449
|
+
|
450
|
+
```ruby
|
451
|
+
def attributes_for_cloning
|
452
|
+
[:name, :slug, :description, :wine_id]
|
453
|
+
end
|
454
|
+
```
|
455
|
+
|
456
|
+
## Cloning Associations
|
457
|
+
|
458
|
+
Belongs_to associations are automatically copied over, unless you are whitelisting attributes and forget to/ purposely don't add it there. For the rest of the associations you may have (i.e. has_one, has_many, has_and_belongs_to_many, has_many_through), you may use the `associations_for_cloning` method by passing in array of symbols.
|
459
|
+
|
460
|
+
**Note:** Any images or files you have will be copied along, if you have included those relationships.
|
461
|
+
|
462
|
+
**Example**
|
463
|
+
|
464
|
+
```ruby
|
465
|
+
def associations_for_cloning
|
466
|
+
[:aromas, :events]
|
467
|
+
end
|
468
|
+
```
|
469
|
+
|
470
|
+
That's it! Happy cloning. :)
|
471
|
+
|