nocms-blocks 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +8 -0
- data/LICENSE +674 -0
- data/README.md +233 -0
- data/Rakefile +22 -0
- data/app/assets/javascripts/no_cms/blocks/application.js +13 -0
- data/app/assets/stylesheets/no_cms/blocks/application.css +13 -0
- data/app/controllers/no_cms/blocks/application_controller.rb +4 -0
- data/app/decorators/models/no_cms/blocks/block_decorator.rb +3 -0
- data/app/helpers/no_cms/blocks/application_helper.rb +4 -0
- data/app/helpers/no_cms/blocks/blocks_helper.rb +28 -0
- data/app/models/no_cms/blocks/block.rb +160 -0
- data/app/models/no_cms/blocks/concerns/translation_scopes.rb +16 -0
- data/app/views/layouts/no_cms/blocks/application.html.erb +14 -0
- data/app/views/no_cms/admin/blocks/blocks/_default.html.erb +9 -0
- data/app/views/no_cms/admin/blocks/blocks/_form.html.erb +25 -0
- data/app/views/no_cms/admin/blocks/blocks/_index.html.erb +15 -0
- data/app/views/no_cms/admin/blocks/blocks/_nested_index.html.erb +17 -0
- data/app/views/no_cms/blocks/blocks/_default.html.erb +8 -0
- data/config/locales/en.yml +15 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20140405135410_create_no_cms_blocks_blocks.rb +21 -0
- data/db/migrate/20140405150944_add_awesome_nested_set_to_no_cms_blocks.rb +8 -0
- data/db/migrate/20140618150651_add_position_to_no_cms_blocks_block.rb +5 -0
- data/lib/generators/nocms/blocks_generator.rb +15 -0
- data/lib/generators/nocms/templates/config/initializers/nocms/blocks.rb +43 -0
- data/lib/no_cms/blocks/configuration.rb +20 -0
- data/lib/no_cms/blocks/engine.rb +17 -0
- data/lib/no_cms/blocks/version.rb +5 -0
- data/lib/nocms-blocks.rb +7 -0
- data/lib/tasks/no_cms/blocks.rake +4 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +8 -0
- data/spec/dummy/app/controllers/home_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/test_image.rb +5 -0
- data/spec/dummy/app/uploaders/logo_uploader.rb +51 -0
- data/spec/dummy/app/views/home/show.html.erb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/no_cms/blocks/blocks/_logo_caption.html.erb +5 -0
- data/spec/dummy/app/views/no_cms/blocks/blocks/_title_3_columns.html.erb +6 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +22 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +38 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/nocms/blocks.rb +43 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140304094052_create_test_images.rb +9 -0
- data/spec/dummy/db/schema.rb +43 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +40 -0
- data/spec/dummy/log/test.log +33500 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/uploads/test_image/logo/1/logo.png +0 -0
- data/spec/dummy/public/uploads/test_image/logo/1/logo2.png +0 -0
- data/spec/factories/no_cms/blocks/block.rb +8 -0
- data/spec/factories/test_image.rb +12 -0
- data/spec/fixtures/images/logo.png +0 -0
- data/spec/fixtures/images/logo2.png +0 -0
- data/spec/models/no_cms/blocks/block_spec.rb +200 -0
- data/spec/requests/no_cms/blocks/blocks_spec.rb +95 -0
- data/spec/spec_helper.rb +70 -0
- data/spec/support/concerns/model_with_has_many_relationship.rb +39 -0
- data/spec/support/concerns/model_with_required_attributes.rb +21 -0
- metadata +236 -0
data/README.md
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
# NoCMS Blocks
|
2
|
+
|
3
|
+
## What's this?
|
4
|
+
|
5
|
+
This is a Rails engine with a basic functionality of customizable blocks of content attachable to objects. It's not attached to any particular CMS so you can use it freely within your Rails application without too much dependencies.
|
6
|
+
|
7
|
+
## How do I install it?
|
8
|
+
|
9
|
+
Right now there's no proper gem, although we have a couple of projects making extensive use of it.
|
10
|
+
|
11
|
+
To install it just put the repo in your Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "nocms-blocks", git: 'git@github.com:simplelogica/nocms-blocks.git'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then import all the migrations:
|
18
|
+
|
19
|
+
```
|
20
|
+
rake no_cms_blocks:install:migrations
|
21
|
+
```
|
22
|
+
|
23
|
+
And run the initializer:
|
24
|
+
|
25
|
+
```
|
26
|
+
rails g nocms:blocks
|
27
|
+
```
|
28
|
+
|
29
|
+
## How does it works?
|
30
|
+
|
31
|
+
Blocks are thought to be independent and customizable modules of content that can be created, edited or removed on their own, without dependency of any other module or class.
|
32
|
+
|
33
|
+
### Block layouts
|
34
|
+
|
35
|
+
In NoCMS Blocks, block layouts define two main things:
|
36
|
+
|
37
|
+
1. What kind of information a block contains and other settings (i.e. cache settings).
|
38
|
+
2. How this information is displayed on a view.
|
39
|
+
|
40
|
+
Block settings are configured in the file `config/initializers/nocms/blocks.rb`. In that file we declare all the available layouts for a block.
|
41
|
+
|
42
|
+
The following code
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
NoCms::Blocks.configure do |config|
|
46
|
+
|
47
|
+
config.block_layouts = {
|
48
|
+
'default' => {
|
49
|
+
template: 'default',
|
50
|
+
fields: {
|
51
|
+
title: :string,
|
52
|
+
body: :text
|
53
|
+
}
|
54
|
+
},
|
55
|
+
'title-3_columns' => {
|
56
|
+
template: 'title_3_columns',
|
57
|
+
fields: {
|
58
|
+
title: :string,
|
59
|
+
column_1: :text,
|
60
|
+
column_2: :text,
|
61
|
+
column_3: :text
|
62
|
+
},
|
63
|
+
},
|
64
|
+
'logo-caption' => {
|
65
|
+
template: 'logo_caption',
|
66
|
+
fields: {
|
67
|
+
caption: :string,
|
68
|
+
logo: TestImage
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
declares 3 layouts ('default', 'title-3_columns' and 'logo-caption'). Each layout has a template and some declared fields. These fields will be available in the ruby object for that block. As an example, if `@block` is an instance of the NoCms::Blocks::Block model which layout attribute is set to 'default' you will be able to do `@block.title`
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
block = NoCms::Blocks::Block.new
|
80
|
+
block.layout = 'default'
|
81
|
+
|
82
|
+
block.title = 'a title'
|
83
|
+
block.title # => 'a title'
|
84
|
+
|
85
|
+
block.column_1 = 'a column' # => NoMethodError
|
86
|
+
block.column_1 # => NoMethodError
|
87
|
+
|
88
|
+
|
89
|
+
block.layout = 'title-3_columns'
|
90
|
+
|
91
|
+
block.title # => 'a title'
|
92
|
+
block.column_1 = 'a column'
|
93
|
+
block.column_1 # => 'a column'
|
94
|
+
block.body # => NoMethodError
|
95
|
+
|
96
|
+
block.layout = 'logo_caption'
|
97
|
+
block.title # => NoMethodError
|
98
|
+
block.logo = { name: 'testing logo' } # Currently this is the way to assign objects
|
99
|
+
block.logo.name # => 'testing logo'
|
100
|
+
block.logo.class # => TestImage
|
101
|
+
block.logo = TestImage.new name: 'testing logo' # Error! Currently assigning the object is not allowed :(
|
102
|
+
```
|
103
|
+
|
104
|
+
### Block templates
|
105
|
+
|
106
|
+
Blocks are rendered using the `render_block` helper which controls all the logic related with renderinf a block, including fragment cache control.
|
107
|
+
|
108
|
+
In the end a partial is rendered using the block as a local variable to obtain the information. This partial must be found at `no_cms/blocks/blocks` views folder and have the name configured in the `template` setting of the block. This way, rendering a 'title-3_columns' would render the partial `/no_cms/blocks/blocks/title_3_columns`.
|
109
|
+
|
110
|
+
This partial is a regular Rails partial (nothing special here). As an example, this could be the content of our `/no_cms/blocks/blocks/title_3_columns.html.erb` partial:
|
111
|
+
|
112
|
+
```html
|
113
|
+
<div class='columns_block'>
|
114
|
+
<h2 class="title"><%= block.title %></h2>
|
115
|
+
<p class="column_1"><%= block.column_1 %></p>
|
116
|
+
<p class="column_2"><%= block.column_2 %></p>
|
117
|
+
<p class="column_3"><%= block.column_3 %></p>
|
118
|
+
</div>
|
119
|
+
```
|
120
|
+
|
121
|
+
As you can see, the partial has a `block` variable containing the block object you are rendering.
|
122
|
+
|
123
|
+
Since this is plain old rails you can do everything you can do with a partial (e.g. having a `/no_cms/blocks/blocks/title_3_columns.en.html.erb` for the english version and a `/no_cms/blocks/blocks/title_3_columns.es.html.erb` for the spanish one).
|
124
|
+
|
125
|
+
### Block Cache
|
126
|
+
|
127
|
+
Since blocks are independent units of content, the standard Rails fragment cache seemed to fit well with them. That's why the `render_block` helper decides wether Rails cache should be used for rendering an individual block.
|
128
|
+
|
129
|
+
Cache for the blocks are configured at 3 levels:
|
130
|
+
|
131
|
+
1. The `render_block` helper may be called with a `cache_enabled` option set to true or false. This option will enable/disable the cache. This allow us to render a block without using the cache (maybe on a preview action).
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
render_block block, cache: false
|
135
|
+
```
|
136
|
+
|
137
|
+
2. In the blocks configuration we can enable/disable the cache for all the blocks of a kind. We just have to add the `cache_enabled` setting.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
NoCms::Blocks.configure do |config|
|
141
|
+
|
142
|
+
config.block_layouts = {
|
143
|
+
'default' => {
|
144
|
+
template: 'default',
|
145
|
+
fields: {
|
146
|
+
title: :string,
|
147
|
+
body: :text
|
148
|
+
},
|
149
|
+
cache_enabled: false
|
150
|
+
}
|
151
|
+
}
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
3. In the blocks configuration file we can enable/disable cache for all the blocks that doesn't have a cache_enabled setting. This configuration will be stored at `NoCms::Blocks.cache_enabled`
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
NoCms::Blocks.configure do |config|
|
159
|
+
|
160
|
+
config.cache_enabled = true
|
161
|
+
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
As a summary:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
|
169
|
+
b = NoCms::Blocks::Block.new layout: 'default', title: 'Foo', description: 'Bar'
|
170
|
+
NoCms::Blocks.cache_enabled # => true
|
171
|
+
b.cache_enabled # => false, since the block configuration sets it to false
|
172
|
+
render_block b # => This won't use fragment cache since this block layout have cache disabled
|
173
|
+
|
174
|
+
b = NoCms::Blocks::Block.new layout: 'title-3_columns', title: 'Foo', description: 'Bar'
|
175
|
+
NoCms::Blocks.cache_enabled # => true
|
176
|
+
b.cache_enabled # => true, since this block configuration doesn't override NoCms::Block.cache_enabled
|
177
|
+
render_block b # => This will use fragment cache since, by default, it's enabled for all blocks
|
178
|
+
|
179
|
+
render_block b, cache_enabled: false # => This won't use fragment cache as the option in the helper overrides the block configuration
|
180
|
+
|
181
|
+
```
|
182
|
+
|
183
|
+
## Where is the admin interface?
|
184
|
+
|
185
|
+
Since blocks are always attached to another object there ¡s no separate admin interface for them.
|
186
|
+
|
187
|
+
Instead of a separate admin interface this engine includes a set of partials that can be used to render it as a basic admin interface attachable to any edit view of any object that has blocks attached.
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
<%= render 'no_cms/admin/blocks/blocks/index', f: f %>
|
191
|
+
```
|
192
|
+
|
193
|
+
### NoCms Admin
|
194
|
+
|
195
|
+
This partial is fully compatible with `nocms-admin` gem and works with the JS in that engine that handles creation or modification of blocks.
|
196
|
+
|
197
|
+
To enable it just add the following code to your form view.
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
<% content_for :ready_js do %>
|
201
|
+
NoCMS.Admin.BlockHandler();
|
202
|
+
<% end %>
|
203
|
+
```
|
204
|
+
|
205
|
+
### Block admin templates
|
206
|
+
|
207
|
+
Same way that a block has a partial that gets rendered in the public views it has another partial to be rendered inside the admin views if you use the no_cms/admin/blocks/blocks/index partial.
|
208
|
+
|
209
|
+
This partial must be found at `no_cms/admin/blocks/blocks` views folder and have the name configured in the `template` setting of the block. This way, rendering a 'title-3_columns' would render the partial `/no_cms/admin/blocks/blocks/title_3_columns`.
|
210
|
+
|
211
|
+
This partial is a regular Rails partial (nothing special here). As an example, this could be the content of our `/no_cms/admin/blocks/blocks/title_3_columns.html.erb` partial:
|
212
|
+
|
213
|
+
```html
|
214
|
+
<div class="row">
|
215
|
+
<%= f.label :title %>
|
216
|
+
<%= f.text_field :title %>
|
217
|
+
</div>
|
218
|
+
|
219
|
+
<div class="row">
|
220
|
+
<%= f.label :column_1 %>
|
221
|
+
<%= f.text_area :column_1 %>
|
222
|
+
</div>
|
223
|
+
|
224
|
+
<div class="row">
|
225
|
+
<%= f.label :column_2 %>
|
226
|
+
<%= f.text_area :column_2 %>
|
227
|
+
</div>
|
228
|
+
|
229
|
+
<div class="row">
|
230
|
+
<%= f.label :column_3 %>
|
231
|
+
<%= f.text_area :column_3 %>
|
232
|
+
</div>
|
233
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'NoCMS Blocks'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
21
|
+
|
22
|
+
task :default => [:"app:spec"]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module NoCms
|
2
|
+
module Blocks
|
3
|
+
module BlocksHelper
|
4
|
+
def render_block block, options = {}
|
5
|
+
return if block.draft && !options[:force_render_draft]
|
6
|
+
# If we don't have any option about cache enabled then we ask the block
|
7
|
+
options[:cache_enabled] = block.cache_enabled unless options.has_key? :cache_enabled
|
8
|
+
|
9
|
+
block_template = "no_cms/blocks/blocks/#{block.template}"
|
10
|
+
|
11
|
+
# And now decide if we use cache or not
|
12
|
+
if options[:cache_enabled]
|
13
|
+
Rails.cache.fetch cache_key_for_blocks(block, block_template, options) do
|
14
|
+
render block_template, block: block
|
15
|
+
end
|
16
|
+
else
|
17
|
+
render block_template, block: block
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache_key_for_blocks block, block_template, options = {}
|
23
|
+
"#{block_template}/#{block.id}/#{block.updated_at.to_i}#{"/#{options[:initial_cache_key]}" unless options[:initial_cache_key] }"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module NoCms::Blocks
|
2
|
+
class Block < ActiveRecord::Base
|
3
|
+
|
4
|
+
include NoCms::Blocks::Concerns::TranslationScopes
|
5
|
+
|
6
|
+
acts_as_nested_set
|
7
|
+
|
8
|
+
scope :drafts, ->() { where_with_locale(draft: true) }
|
9
|
+
scope :no_drafts, ->() { where_with_locale(draft: false) }
|
10
|
+
scope :roots, ->() { where parent_id: nil }
|
11
|
+
|
12
|
+
accepts_nested_attributes_for :children, allow_destroy: true
|
13
|
+
|
14
|
+
attr_reader :cached_objects
|
15
|
+
|
16
|
+
translates :layout, :fields_info, :draft
|
17
|
+
accepts_nested_attributes_for :translations
|
18
|
+
|
19
|
+
class Translation
|
20
|
+
serialize :fields_info, Hash
|
21
|
+
end
|
22
|
+
|
23
|
+
after_initialize :set_blank_fields
|
24
|
+
before_save :save_related_objects
|
25
|
+
|
26
|
+
validates :fields_info, presence: { allow_blank: true }
|
27
|
+
validates :layout, presence: true
|
28
|
+
|
29
|
+
def cache_enabled
|
30
|
+
layout_config.has_key?(:cache_enabled) ? layout_config[:cache_enabled] : NoCms::Blocks.cache_enabled
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def layout_config
|
35
|
+
NoCms::Blocks.block_layouts.stringify_keys[layout]
|
36
|
+
end
|
37
|
+
|
38
|
+
def template
|
39
|
+
layout_config[:template] if layout_config
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_field? field
|
43
|
+
# We have the field if...
|
44
|
+
!layout_config.nil? && # We have a layout configuration AND
|
45
|
+
(
|
46
|
+
!layout_config[:fields].symbolize_keys[field.to_sym].nil? || # We have this field OR
|
47
|
+
!layout_config[:fields].symbolize_keys[field.to_s.gsub(/\_id$/, '').to_sym].nil? # we remove the final _id and then we have the field
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def field_type field
|
52
|
+
return nil unless has_field?(field)
|
53
|
+
layout_config[:fields].symbolize_keys[field.to_sym]
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_field field
|
57
|
+
return nil unless has_field?(field)
|
58
|
+
|
59
|
+
value = fields_info[field.to_sym] || # first, we get the value
|
60
|
+
@cached_objects[field.to_sym] # or we get it from the cached objects
|
61
|
+
|
62
|
+
# If value is still nil, but the field exists we must get the object from the database
|
63
|
+
if value.nil?
|
64
|
+
field_type = field_type(field)
|
65
|
+
field_id = fields_info["#{field}_id".to_sym]
|
66
|
+
value = @cached_objects[field.to_sym] = field_type.find(field_id) unless field_id.nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
# If value is still nil, and the field_type is an ActiveRecord class, then we
|
70
|
+
if value.nil? && field_type.is_a?(Class)
|
71
|
+
value = @cached_objects[field.to_sym] = field_type.new
|
72
|
+
end
|
73
|
+
value
|
74
|
+
end
|
75
|
+
|
76
|
+
def write_field field, value
|
77
|
+
return nil unless has_field?(field)
|
78
|
+
field_type = field_type field
|
79
|
+
# If field type is a model then we update the cached object
|
80
|
+
if field_type.is_a?(Class) && field_type < ActiveRecord::Base
|
81
|
+
# First, we initialize the object if we don't read the object (it loads it into the cached objects)
|
82
|
+
@cached_objects[field.to_sym] = field_type.new if read_field(field).nil?
|
83
|
+
# Then, assign attributes
|
84
|
+
@cached_objects[field.to_sym].assign_attributes value
|
85
|
+
else # If it's a model then a new object or update the previous one
|
86
|
+
self.fields_info = fields_info.merge field.to_sym => value # when updating through an object (i.e. the page updates through nested attributes) fields_info[field.to_sym] = value doesn't work. Kudos to Rubo for this fix
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# In this missing method we check wether we're asking for one field
|
91
|
+
# in which case we will read or write ir
|
92
|
+
def method_missing(m, *args, &block)
|
93
|
+
# We get the name of the field stripping out the '=' for writers
|
94
|
+
field = m.to_s
|
95
|
+
write_accessor = field.ends_with? '='
|
96
|
+
field.gsub!(/\=$/, '')
|
97
|
+
|
98
|
+
# If this field actually exists, then we write it or read it.
|
99
|
+
if has_field?(field)
|
100
|
+
write_accessor ?
|
101
|
+
write_field(field, args.first) :
|
102
|
+
read_field(field.to_sym)
|
103
|
+
else
|
104
|
+
super
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# When we are assigning attributes (this method is called in new, create...)
|
109
|
+
# we must split those fields from our current layout and those who are not
|
110
|
+
# (they must be attributes).
|
111
|
+
# Attributes are processed the usual way and fields are written later
|
112
|
+
def assign_attributes new_attributes
|
113
|
+
fields = []
|
114
|
+
|
115
|
+
set_blank_fields
|
116
|
+
|
117
|
+
# We get the layout
|
118
|
+
new_layout = new_attributes[:layout] || new_attributes['layout']
|
119
|
+
self.layout = new_layout unless new_layout.nil?
|
120
|
+
|
121
|
+
Rails.logger.info "Searching #{new_attributes.keys.inspect} fields in #{self.layout} layout"
|
122
|
+
|
123
|
+
# And now separate fields and attributes
|
124
|
+
fields = new_attributes.select{|k, _| has_field? k }.symbolize_keys
|
125
|
+
new_attributes.reject!{|k, _| has_field? k }
|
126
|
+
|
127
|
+
super(new_attributes)
|
128
|
+
|
129
|
+
Rails.logger.info "Writing #{fields.inspect} to #{self.layout} block"
|
130
|
+
|
131
|
+
fields.each do |field_name, value|
|
132
|
+
self.write_field field_name, value
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def reload *args
|
137
|
+
@cached_objects = {}
|
138
|
+
super
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def set_blank_fields
|
144
|
+
self.fields_info ||= {}
|
145
|
+
@cached_objects ||= {}
|
146
|
+
end
|
147
|
+
|
148
|
+
def save_related_objects
|
149
|
+
# Now we save each activerecord related object
|
150
|
+
cached_objects.each do |field, object|
|
151
|
+
# Notice that we don't care if the object is actually saved
|
152
|
+
# We don't care because there may be some cases where no real information is sent to an object but something is sent (i.e. the locale in a new Globalize translation) and then the object is created empty
|
153
|
+
# When this happens if we save! the object an error is thrown and we can't leave the object blank
|
154
|
+
if object.is_a?(ActiveRecord::Base) && object.save
|
155
|
+
fields_info["#{field}_id".to_sym] = object.id
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|