nocms-blocks 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +10 -0
  3. data/README.md +7 -3
  4. data/app/models/no_cms/blocks/block.rb +31 -128
  5. data/app/models/no_cms/blocks/concerns/serializing_fields.rb +386 -0
  6. data/app/models/no_cms/blocks/layout.rb +98 -0
  7. data/app/views/no_cms/blocks/blocks/_default.html.erb +6 -0
  8. data/db/migrate/20150709132202_add_non_translated_fields_info_to_no_cms_blocks_block.rb +5 -0
  9. data/db/migrate/20150710112549_move_layout_from_no_cms_blocks_block_translations_to_no_cms_blocks_blocks.rb +19 -0
  10. data/lib/generators/nocms/templates/config/initializers/nocms/blocks.rb +8 -0
  11. data/lib/no_cms/blocks/version.rb +1 -1
  12. data/spec/dummy/db/development.sqlite3 +0 -0
  13. data/spec/dummy/db/schema.rb +3 -2
  14. data/spec/dummy/db/test.sqlite3 +0 -0
  15. data/spec/dummy/log/development.log +163 -0
  16. data/spec/dummy/log/test.log +94024 -0
  17. data/spec/dummy/public/uploads/test_image/logo/2/logo.png +0 -0
  18. data/spec/dummy/public/uploads/test_image/logo/2/logo2.png +0 -0
  19. data/spec/dummy/public/uploads/test_image/logo/3/logo.png +0 -0
  20. data/spec/dummy/public/uploads/test_image/logo/4/logo.png +0 -0
  21. data/spec/dummy/public/uploads/test_image/logo/5/logo.png +0 -0
  22. data/spec/dummy/public/uploads/test_image/logo/6/logo.png +0 -0
  23. data/spec/dummy/public/uploads/tmp/1436194769-17662-1395/logo.png +0 -0
  24. data/spec/dummy/public/uploads/tmp/1436194769-17662-1590/logo.png +0 -0
  25. data/spec/dummy/public/uploads/tmp/1436194769-17662-1891/logo.png +0 -0
  26. data/spec/dummy/public/uploads/tmp/1436194769-17662-1891/logo2.png +0 -0
  27. data/spec/dummy/public/uploads/tmp/1436194769-17662-2425/logo.png +0 -0
  28. data/spec/dummy/public/uploads/tmp/1436194769-17662-3650/logo.png +0 -0
  29. data/spec/dummy/public/uploads/tmp/1436194769-17662-3867/logo.png +0 -0
  30. data/spec/dummy/public/uploads/tmp/1436194769-17662-3867/logo2.png +0 -0
  31. data/spec/dummy/public/uploads/tmp/1436194769-17662-5629/logo.png +0 -0
  32. data/spec/dummy/public/uploads/tmp/1436194769-17662-5795/logo.png +0 -0
  33. data/spec/dummy/public/uploads/tmp/1436194769-17662-5882/logo.png +0 -0
  34. data/spec/dummy/public/uploads/tmp/1436194769-17662-9375/logo.png +0 -0
  35. data/spec/dummy/public/uploads/tmp/1436194790-17727-0450/logo.png +0 -0
  36. data/spec/dummy/public/uploads/tmp/1436194790-17727-0733/logo.png +0 -0
  37. data/spec/dummy/public/uploads/tmp/1436194790-17727-0733/logo2.png +0 -0
  38. data/spec/dummy/public/uploads/tmp/1436194790-17727-1087/logo.png +0 -0
  39. data/spec/dummy/public/uploads/tmp/1436194790-17727-1087/logo2.png +0 -0
  40. data/spec/dummy/public/uploads/tmp/1436194790-17727-1788/logo.png +0 -0
  41. data/spec/dummy/public/uploads/tmp/1436194790-17727-2101/logo.png +0 -0
  42. data/spec/dummy/public/uploads/tmp/1436194790-17727-4443/logo.png +0 -0
  43. data/spec/dummy/public/uploads/tmp/1436194790-17727-5065/logo.png +0 -0
  44. data/spec/dummy/public/uploads/tmp/1436194790-17727-6456/logo.png +0 -0
  45. data/spec/dummy/public/uploads/tmp/1436194790-17727-8131/logo.png +0 -0
  46. data/spec/dummy/public/uploads/tmp/1436194790-17727-9096/logo.png +0 -0
  47. data/spec/dummy/public/uploads/tmp/1436194856-17785-0815/logo.png +0 -0
  48. data/spec/dummy/public/uploads/tmp/1436194856-17785-4789/logo.png +0 -0
  49. data/spec/dummy/public/uploads/tmp/1436194856-17785-7685/logo.png +0 -0
  50. data/spec/dummy/public/uploads/tmp/1436194857-17785-0259/logo.png +0 -0
  51. data/spec/dummy/public/uploads/tmp/1436194857-17785-0349/logo.png +0 -0
  52. data/spec/dummy/public/uploads/tmp/1436194857-17785-0476/logo.png +0 -0
  53. data/spec/dummy/public/uploads/tmp/1436194857-17785-1997/logo.png +0 -0
  54. data/spec/dummy/public/uploads/tmp/1436194857-17785-3813/logo.png +0 -0
  55. data/spec/dummy/public/uploads/tmp/1436194857-17785-3813/logo2.png +0 -0
  56. data/spec/dummy/public/uploads/tmp/1436194857-17785-4455/logo.png +0 -0
  57. data/spec/dummy/public/uploads/tmp/1436194857-17785-5316/logo.png +0 -0
  58. data/spec/dummy/public/uploads/tmp/1436194857-17785-5316/logo2.png +0 -0
  59. data/spec/dummy/public/uploads/tmp/1436194857-17785-7121/logo.png +0 -0
  60. data/spec/dummy/public/uploads/tmp/1436194857-17785-7447/logo.png +0 -0
  61. data/spec/dummy/public/uploads/tmp/1436194857-17785-7677/logo.png +0 -0
  62. data/spec/dummy/public/uploads/tmp/1436194857-17785-8037/logo.png +0 -0
  63. data/spec/dummy/public/uploads/tmp/1436194857-17785-8791/logo.png +0 -0
  64. data/spec/dummy/public/uploads/tmp/1436194878-17844-1352/logo.png +0 -0
  65. data/spec/dummy/public/uploads/tmp/1436194878-17844-1557/logo.png +0 -0
  66. data/spec/dummy/public/uploads/tmp/1436194878-17844-5177/logo.png +0 -0
  67. data/spec/dummy/public/uploads/tmp/1436194878-17844-5338/logo.png +0 -0
  68. data/spec/dummy/public/uploads/tmp/1436194878-17844-8747/logo.png +0 -0
  69. data/spec/dummy/public/uploads/tmp/1436194879-17844-3191/logo.png +0 -0
  70. data/spec/dummy/public/uploads/tmp/1436194879-17844-3986/logo.png +0 -0
  71. data/spec/dummy/public/uploads/tmp/1436194879-17844-5951/logo.png +0 -0
  72. data/spec/dummy/public/uploads/tmp/1436194879-17844-6104/logo.png +0 -0
  73. data/spec/dummy/public/uploads/tmp/1436194879-17844-7114/logo.png +0 -0
  74. data/spec/dummy/public/uploads/tmp/1436194879-17844-7114/logo2.png +0 -0
  75. data/spec/dummy/public/uploads/tmp/1436194879-17844-7617/logo.png +0 -0
  76. data/spec/dummy/public/uploads/tmp/1436194879-17844-8185/logo.png +0 -0
  77. data/spec/dummy/public/uploads/tmp/1436194879-17844-8495/logo.png +0 -0
  78. data/spec/dummy/public/uploads/tmp/1436194879-17844-8495/logo2.png +0 -0
  79. data/spec/dummy/public/uploads/tmp/1436194879-17844-8991/logo.png +0 -0
  80. data/spec/dummy/public/uploads/tmp/1436194879-17844-9466/logo.png +0 -0
  81. data/spec/dummy/public/uploads/tmp/1436194892-17862-1771/logo.png +0 -0
  82. data/spec/dummy/public/uploads/tmp/1436194926-17904-9747/logo.png +0 -0
  83. data/spec/dummy/public/uploads/tmp/1436195033-17981-2265/logo.png +0 -0
  84. data/spec/dummy/public/uploads/tmp/1436195045-18015-3603/logo.png +0 -0
  85. data/spec/dummy/public/uploads/tmp/1436195085-18055-8412/logo.png +0 -0
  86. data/spec/dummy/public/uploads/tmp/1436195101-18097-0044/logo.png +0 -0
  87. data/spec/dummy/public/uploads/tmp/1436195126-18165-6939/logo.png +0 -0
  88. data/spec/dummy/public/uploads/tmp/1436195152-18225-1850/logo.png +0 -0
  89. data/spec/dummy/public/uploads/tmp/1436195169-18262-2283/logo.png +0 -0
  90. data/spec/dummy/public/uploads/tmp/1436195186-18300-3395/logo.png +0 -0
  91. data/spec/dummy/public/uploads/tmp/1436195205-18336-0453/logo.png +0 -0
  92. data/spec/dummy/public/uploads/tmp/1436195242-18389-3554/logo.png +0 -0
  93. data/spec/dummy/public/uploads/tmp/1436195255-18420-9336/logo.png +0 -0
  94. data/spec/dummy/public/uploads/tmp/1436195745-18605-6163/logo.png +0 -0
  95. data/spec/dummy/public/uploads/tmp/1436195823-18669-7443/logo.png +0 -0
  96. data/spec/dummy/public/uploads/tmp/1436195886-18739-5334/logo.png +0 -0
  97. data/spec/dummy/public/uploads/tmp/1436195900-18776-9749/logo.png +0 -0
  98. data/spec/dummy/public/uploads/tmp/1436195935-18824-1992/logo.png +0 -0
  99. data/spec/dummy/public/uploads/tmp/1436196060-18894-4158/logo.png +0 -0
  100. data/spec/dummy/public/uploads/tmp/1436196106-18943-1035/logo.png +0 -0
  101. data/spec/dummy/public/uploads/tmp/1436196156-18987-1445/logo.png +0 -0
  102. data/spec/dummy/public/uploads/tmp/1436196187-19045-9003/logo.png +0 -0
  103. data/spec/dummy/public/uploads/tmp/1436196275-19125-3257/logo.png +0 -0
  104. data/spec/dummy/public/uploads/tmp/1436196321-19169-5591/logo.png +0 -0
  105. data/spec/dummy/public/uploads/tmp/1436258374-8961-0160/logo.png +0 -0
  106. data/spec/dummy/public/uploads/tmp/1436258374-8961-0565/logo.png +0 -0
  107. data/spec/dummy/public/uploads/tmp/1436258374-8961-1519/logo.png +0 -0
  108. data/spec/dummy/public/uploads/tmp/1436258374-8961-2202/logo.png +0 -0
  109. data/spec/dummy/public/uploads/tmp/1436258374-8961-2400/logo.png +0 -0
  110. data/spec/dummy/public/uploads/tmp/1436258374-8961-2928/logo.png +0 -0
  111. data/spec/dummy/public/uploads/tmp/1436258374-8961-3260/logo.png +0 -0
  112. data/spec/dummy/public/uploads/tmp/1436258374-8961-4333/logo.png +0 -0
  113. data/spec/dummy/public/uploads/tmp/1436258374-8961-5122/logo.png +0 -0
  114. data/spec/dummy/public/uploads/tmp/1436258374-8961-6333/logo.png +0 -0
  115. data/spec/dummy/public/uploads/tmp/1436258374-8961-6333/logo2.png +0 -0
  116. data/spec/dummy/public/uploads/tmp/1436258374-8961-7928/logo.png +0 -0
  117. data/spec/dummy/public/uploads/tmp/1436258374-8961-9359/logo.png +0 -0
  118. data/spec/dummy/public/uploads/tmp/1436258374-8961-9392/logo.png +0 -0
  119. data/spec/dummy/public/uploads/tmp/1436258374-8961-9549/logo.png +0 -0
  120. data/spec/dummy/public/uploads/tmp/1436258374-8961-9549/logo2.png +0 -0
  121. data/spec/dummy/public/uploads/tmp/1436258374-8961-9648/logo.png +0 -0
  122. data/spec/dummy/public/uploads/tmp/1436258382-8967-0797/logo.png +0 -0
  123. data/spec/dummy/public/uploads/tmp/1436258453-9165-7375/logo.png +0 -0
  124. data/spec/dummy/public/uploads/tmp/1436258549-9282-2039/logo.png +0 -0
  125. data/spec/dummy/public/uploads/tmp/1436258623-9355-6670/logo.png +0 -0
  126. data/spec/dummy/public/uploads/tmp/1436258674-9394-2929/logo.png +0 -0
  127. data/spec/dummy/public/uploads/tmp/1436264869-10860-2169/logo.png +0 -0
  128. data/spec/dummy/public/uploads/tmp/1436276653-16968-2375/logo.png +0 -0
  129. data/spec/dummy/public/uploads/tmp/1436276734-17282-9777/logo.png +0 -0
  130. data/spec/dummy/public/uploads/tmp/1436276770-17431-4333/logo.png +0 -0
  131. data/spec/dummy/public/uploads/tmp/1436276781-17463-5971/logo.png +0 -0
  132. data/spec/dummy/public/uploads/tmp/1436276875-17556-1972/logo.png +0 -0
  133. data/spec/dummy/public/uploads/tmp/1436276882-17588-9186/logo.png +0 -0
  134. data/spec/dummy/public/uploads/tmp/1436276896-17657-9942/logo.png +0 -0
  135. data/spec/dummy/public/uploads/tmp/1436277157-17795-9344/logo.png +0 -0
  136. data/spec/dummy/public/uploads/tmp/1436277178-17833-8883/logo.png +0 -0
  137. data/spec/dummy/public/uploads/tmp/1436277781-18431-2830/logo.png +0 -0
  138. data/spec/dummy/public/uploads/tmp/1436278571-18860-1079/logo.png +0 -0
  139. data/spec/dummy/public/uploads/tmp/1436282613-22892-0135/logo.png +0 -0
  140. data/spec/dummy/public/uploads/tmp/1436282638-22953-7833/logo.png +0 -0
  141. data/spec/dummy/public/uploads/tmp/1436282887-23026-0068/logo.png +0 -0
  142. data/spec/dummy/public/uploads/tmp/1436282978-23160-8886/logo.png +0 -0
  143. data/spec/dummy/public/uploads/tmp/1436284256-24634-1740/logo.png +0 -0
  144. data/spec/dummy/public/uploads/tmp/1436284256-24634-3170/logo.png +0 -0
  145. data/spec/dummy/public/uploads/tmp/1436284256-24634-3501/logo.png +0 -0
  146. data/spec/dummy/public/uploads/tmp/1436284256-24634-6393/logo.png +0 -0
  147. data/spec/dummy/public/uploads/tmp/1436284256-24634-6862/logo.png +0 -0
  148. data/spec/dummy/public/uploads/tmp/1436284256-24634-7741/logo.png +0 -0
  149. data/spec/dummy/public/uploads/tmp/1436284256-24634-8051/logo.png +0 -0
  150. data/spec/dummy/public/uploads/tmp/1436284256-24634-8064/logo.png +0 -0
  151. data/spec/dummy/public/uploads/tmp/1436284256-24634-8093/logo.png +0 -0
  152. data/spec/dummy/public/uploads/tmp/1436284256-24634-9497/logo.png +0 -0
  153. data/spec/dummy/public/uploads/tmp/1436553862-9485-0200/logo.png +0 -0
  154. data/spec/dummy/public/uploads/tmp/1436553862-9485-0325/logo.png +0 -0
  155. data/spec/dummy/public/uploads/tmp/1436553862-9485-1380/logo.png +0 -0
  156. data/spec/dummy/public/uploads/tmp/1436553862-9485-4150/logo.png +0 -0
  157. data/spec/dummy/public/uploads/tmp/1436553862-9485-4347/logo.png +0 -0
  158. data/spec/dummy/public/uploads/tmp/1436553862-9485-5072/logo.png +0 -0
  159. data/spec/dummy/public/uploads/tmp/1436553862-9485-5891/logo2.png +0 -0
  160. data/spec/dummy/public/uploads/tmp/1436553862-9485-6304/logo.png +0 -0
  161. data/spec/dummy/public/uploads/tmp/1436553862-9485-6620/logo.png +0 -0
  162. data/spec/dummy/public/uploads/tmp/1436553862-9485-6703/logo.png +0 -0
  163. data/spec/dummy/public/uploads/tmp/1436553862-9485-8254/logo.png +0 -0
  164. data/spec/dummy/public/uploads/tmp/1436553862-9485-8313/logo2.png +0 -0
  165. data/spec/dummy/public/uploads/tmp/1436554155-9646-0021/logo.png +0 -0
  166. data/spec/dummy/public/uploads/tmp/1436554155-9646-1865/logo.png +0 -0
  167. data/spec/dummy/public/uploads/tmp/1436554155-9646-2510/logo.png +0 -0
  168. data/spec/dummy/public/uploads/tmp/1436554155-9646-3771/logo.png +0 -0
  169. data/spec/dummy/public/uploads/tmp/1436554155-9646-4441/logo.png +0 -0
  170. data/spec/dummy/public/uploads/tmp/1436554155-9646-5712/logo.png +0 -0
  171. data/spec/dummy/public/uploads/tmp/1436554155-9646-6456/logo2.png +0 -0
  172. data/spec/dummy/public/uploads/tmp/1436554155-9646-6520/logo.png +0 -0
  173. data/spec/dummy/public/uploads/tmp/1436554155-9646-8183/logo.png +0 -0
  174. data/spec/dummy/public/uploads/tmp/1436554155-9646-8675/logo.png +0 -0
  175. data/spec/dummy/public/uploads/tmp/1436554155-9646-8953/logo.png +0 -0
  176. data/spec/dummy/public/uploads/tmp/1436554155-9646-9271/logo2.png +0 -0
  177. data/spec/dummy/public/uploads/tmp/1436554181-9685-7099/logo.png +0 -0
  178. data/spec/dummy/public/uploads/tmp/1436554233-9708-0769/logo.png +0 -0
  179. data/spec/dummy/public/uploads/tmp/1436554254-9739-7026/logo.png +0 -0
  180. data/spec/dummy/public/uploads/tmp/1436554350-9834-3927/logo.png +0 -0
  181. data/spec/dummy/public/uploads/tmp/1436554416-9875-0666/logo.png +0 -0
  182. data/spec/dummy/public/uploads/tmp/1436554519-9921-2596/logo.png +0 -0
  183. data/spec/dummy/public/uploads/tmp/1436554569-9980-0004/logo.png +0 -0
  184. data/spec/dummy/public/uploads/tmp/1436554694-10292-1775/logo2.png +0 -0
  185. data/spec/dummy/public/uploads/tmp/1436554694-10292-5549/logo2.png +0 -0
  186. data/spec/dummy/public/uploads/tmp/1436554722-10308-2875/logo2.png +0 -0
  187. data/spec/dummy/public/uploads/tmp/1436554732-10340-8854/logo2.png +0 -0
  188. data/spec/dummy/public/uploads/tmp/1436554994-10463-9859/logo2.png +0 -0
  189. data/spec/dummy/public/uploads/tmp/1436555134-10554-3506/logo2.png +0 -0
  190. data/spec/dummy/public/uploads/tmp/1436555227-10604-7612/logo2.png +0 -0
  191. data/spec/dummy/public/uploads/tmp/1436555450-10700-1633/logo2.png +0 -0
  192. data/spec/models/no_cms/blocks/duplicating_blocks_spec.rb +161 -0
  193. data/spec/models/no_cms/blocks/i18n_blocks_spec.rb +101 -0
  194. data/spec/models/no_cms/blocks/layout_spec.rb +125 -0
  195. data/spec/spec_helper.rb +3 -0
  196. metadata +385 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1a4d1f3cc485767a9980d24903923abc0e0cb81
4
- data.tar.gz: 9af340e08b7aaf235058f114877d040216d005bd
3
+ metadata.gz: bbc9eae202e6ec4df5bdba9d91c4a842e03e2f3d
4
+ data.tar.gz: 2523df1872e26d06a714b3bc9c8552c49c07c279
5
5
  SHA512:
6
- metadata.gz: 29c7920341189899327e206dbe3581996a35304580d33e5d6213c5abc34909c3e3faceb5f63599b3c5503dfe731d12d320ff50d710387bab57d6f34f25e66028
7
- data.tar.gz: fa197c2daa8a2a407551c923c58eebe71d76b703de6fbc1cc4f18f2239165f292d97f86bc704c55b5ded6ce05e613d0b2d0d3d7f88714af91285694228e3f6ba
6
+ metadata.gz: 841a55342e8a5b30e2a4c4a5c2771d9d231a4ce68fbe483896159823d44332130fc2b09b8ba214927cb395858301565bac8ef03a98e34c9df8e1ca67b7911ec3
7
+ data.tar.gz: cefb0459db81a0af3dd4e10f3da524b6c6d5359a0aecfe5b1b6cd5664d30c1d09315bc0a5615dab8ad2cea01f16cc5ffd5a3f0b2bc9a0c36c37b883529e0c1fe
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ 1.1.0
2
+
3
+ - I18n behaviour fixed
4
+ * Separated objects cache for translations
5
+ * fields info both in the block and the translation
6
+
7
+ - Globalize dependency correctly specified in the gemspec
8
+
9
+ - Latest versions of awesome-nested-set (3.0.2) and globalize (5.0.1)
10
+
1
11
  1.0.0
2
12
 
3
13
  - First released version.
data/README.md CHANGED
@@ -6,15 +6,19 @@ This is a Rails engine with a basic functionality of customizable blocks of cont
6
6
 
7
7
  ## How do I install it?
8
8
 
9
- Right now there's no proper gem, although we have a couple of projects making extensive use of it.
9
+ Just include in your Gemfile:
10
10
 
11
- To install it just put the repo in your Gemfile:
11
+ ```ruby
12
+ gem "nocms-blocks"
13
+ ```
14
+
15
+ If you're brave and want to use the last development version you can use:
12
16
 
13
17
  ```ruby
14
18
  gem "nocms-blocks", git: 'git@github.com:simplelogica/nocms-blocks.git'
15
19
  ```
16
20
 
17
- And then import all the migrations:
21
+ Once the gem is installed you can import all the migrations:
18
22
 
19
23
  ```
20
24
  rake no_cms_blocks:install:migrations
@@ -9,152 +9,55 @@ module NoCms::Blocks
9
9
  scope :no_drafts, ->() { where_with_locale(draft: false) }
10
10
  scope :roots, ->() { where parent_id: nil }
11
11
 
12
- accepts_nested_attributes_for :children, allow_destroy: true
13
-
14
- attr_reader :cached_objects
12
+ translates :draft
13
+ include NoCms::Blocks::Concerns::SerializingFields
15
14
 
16
- translates :layout, :fields_info, :draft
17
- accepts_nested_attributes_for :translations
18
-
19
- class Translation
20
- serialize :fields_info, Hash
15
+ ##
16
+ # In the block we get all the fields so it can accept all of them
17
+ def fields_configuration
18
+ layout_config.fields
21
19
  end
22
20
 
23
- after_initialize :set_blank_fields
24
- before_save :save_related_objects
25
-
26
- validates :fields_info, presence: { allow_blank: true }
27
21
  validates :layout, presence: true
28
22
 
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
23
+ ##
24
+ # A block dups all it's children and the translations
25
+ def duplicate_self new_self
37
26
 
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
27
+ new_self.translations = translations.map(&:dup)
28
+ new_self.translations.each { |t| t.globalized_model = new_self }
68
29
 
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
30
+ children.each do |child|
31
+ new_self.children << child.dup
72
32
  end
73
- value
74
33
  end
75
34
 
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
35
+ class Translation
89
36
 
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
37
+ ##
38
+ # In the translation we get only the translated fields
39
+ def fields_configuration
40
+ layout_config.translated_fields
105
41
  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
42
 
127
- super(new_attributes)
43
+ ##
44
+ # Sometimes the translation still won't have the globalized_model linked
45
+ # (e.g. before it's saved for the first time) and we must have a mechanism
46
+ # to be able to store a temporary layout
47
+ attr_accessor :layout
128
48
 
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
49
+ ##
50
+ # If we don't have a globalized model yet we return our temporary layout
51
+ def layout
52
+ globalized_model.nil? ? @layout : globalized_model.layout
133
53
  end
134
- end
135
54
 
136
- def reload *args
137
- @cached_objects = {}
138
- super
55
+ include NoCms::Blocks::Concerns::SerializingFields
139
56
  end
140
57
 
141
- private
142
-
143
- def set_blank_fields
144
- self.fields_info ||= {}
145
- @cached_objects ||= {}
146
- end
58
+ accepts_nested_attributes_for :children, allow_destroy: true
59
+ accepts_nested_attributes_for :translations
147
60
 
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
61
  end
62
+
160
63
  end
@@ -0,0 +1,386 @@
1
+ module NoCms
2
+ module Blocks
3
+ module Concerns
4
+ module SerializingFields
5
+ extend ActiveSupport::Concern
6
+
7
+ self.included do
8
+
9
+ serialize :fields_info, Hash
10
+
11
+ after_initialize :set_blank_fields
12
+
13
+ before_save :save_related_objects
14
+
15
+ validates :fields_info, presence: { allow_blank: true }
16
+
17
+ ##
18
+ # This attribute stores all the objects referenced on those fields
19
+ # from an AR subtype.
20
+ #
21
+ # It acts both as a cache and as a way to edit or create AR objects
22
+ # and save them when the block is saved.
23
+ attr_reader :cached_objects
24
+
25
+
26
+ ##
27
+ # This method checks wether the block's layout has cache configured
28
+ # and returns it.
29
+ #
30
+ # If it hasn't it returns the global cache_enabled configuration for
31
+ # NoCms::Blocks
32
+ def cache_enabled?
33
+ layout_config.cache_enabled?
34
+ end
35
+
36
+ ##
37
+ # Old version without '?' mantained for historical reasons
38
+ alias_method :cache_enabled, :cache_enabled?
39
+
40
+ ##
41
+ # Returns a hash with the layout information read from the blocks
42
+ # initializer and the 'layout' field on the corresponding object.
43
+ def layout_config
44
+ return if layout.nil?
45
+ @layout_config ||= NoCms::Blocks::Layout.find layout
46
+ end
47
+
48
+ ##
49
+ # Returns the template read from the layout configuration
50
+ def template
51
+ layout_config.template if layout_config
52
+ end
53
+
54
+ ##
55
+ # This method checks that we have the field passed as parameter in our
56
+ # layout configuration.
57
+ #
58
+ # It also takes into account the case where we have an AR object and
59
+ # we're asking just for its id.
60
+ def has_field? field
61
+ # We have the field if...
62
+ !layout_config.nil? && # We have a layout configuration AND
63
+ !layout_config.field(field).nil? # We have this field
64
+ end
65
+
66
+ ##
67
+ # This method tells wether we are in a translation and we must manage
68
+ # translated fields or not
69
+ def is_translation?
70
+ !self.respond_to?(:translations)
71
+ end
72
+
73
+ ##
74
+ # Returns the type of a field in the current layout configuration of
75
+ # this block.
76
+ #
77
+ # If the field is not present in the layout configuration it returns
78
+ # nil.
79
+ def field_type field
80
+ return nil unless has_field?(field)
81
+ fields_configuration.symbolize_keys[field.to_sym][:type]
82
+ end
83
+
84
+ ##
85
+ # Returns the stored value of the field for this block.
86
+ #
87
+ # If the field is not present in the layout configuration it returns
88
+ # nil.
89
+ #
90
+ # If the field is an Active Record object but it's not present on our
91
+ # objects cache we fetch it from the database using the id stored in
92
+ # the #{field}_id field.
93
+ #
94
+ # If it's an Active Record object but we don't have the #{field}_id
95
+ # field then it creates (with new, not with create) a new one and
96
+ # stores it in the objects cache. Later, if the block is saved, this
97
+ # object will be saved too.
98
+ def read_field field
99
+ raise NoMethodError.new("field #{field} is not defined in the block layout") unless has_field?(field)
100
+
101
+ # first, we get the value
102
+ value = fields_info[field.to_sym] ||
103
+ # or we get it from the cached objects
104
+ @cached_objects[field.to_sym]
105
+
106
+ # If value is still nil, but the field exists we must get the object
107
+ # from the database
108
+ if value.nil?
109
+ field_type = field_type(field)
110
+ field_id = fields_info["#{field}_id".to_sym]
111
+ unless field_id.nil?
112
+ value = field_type.find(field_id)
113
+ @cached_objects[field.to_sym] = value
114
+ end
115
+ end
116
+
117
+ # If value is still nil, and the field_type is an ActiveRecord
118
+ # class, then we build a new one
119
+ if value.nil? && field_type.is_a?(Class)
120
+ value = field_type.new
121
+ @cached_objects[field.to_sym] = value
122
+ end
123
+ value
124
+ end
125
+
126
+ ##
127
+ # This method stores the parameter value into the corresponding field.
128
+ #
129
+ # If the field is an Active Record object then we load it into the
130
+ # objects cache and assign it the value through an assign_attributes.
131
+ # This solves the scenario of a nested form where a hash is passed as
132
+ # the value of the field.
133
+ def write_field field, value
134
+ raise NoMethodError.new("field #{field} is not defined in the block layout") unless has_field?(field)
135
+ field_type = field_type field
136
+ # If field type is a model then we update the cached object
137
+ if field_type.is_a?(Class) && field_type < ActiveRecord::Base
138
+
139
+ # We read the object and assign it the attributes attributes.
140
+ # Since we use the read_field method it will take into account
141
+ # if the AR object needs to be build
142
+ read_field(field).assign_attributes value
143
+
144
+ # Even if the fields_info has not changed we need to store the
145
+ # modification, so an association may be saved in cascade (e.g.
146
+ # the translation of a block would not be saved if we don't force
147
+ # this)
148
+ fields_info_will_change!
149
+ else
150
+ # If it's not a model then we merge with the previous value
151
+
152
+ # when updating through an object (i.e. the page updates through
153
+ # nested attributes) fields_info[field.to_sym] = value doesn't
154
+ # work. Kudos to Rubo for this fix
155
+ self.fields_info = fields_info.nil? ?
156
+ { field.to_sym => value } :
157
+ fields_info.merge(field.to_sym => value)
158
+
159
+ end
160
+ end
161
+
162
+ ##
163
+ # This method duplicates a field and stores its value.
164
+ #
165
+ # It takes into account that the field may be an AR object and updates
166
+ # the cached objects.
167
+ #
168
+ # We have different options of duplication depending on the field's
169
+ # configuration:
170
+ #
171
+ # * duplication: It's the default behaviour. It just performs a dup
172
+ # of the field and expects the attached object to implement dup in
173
+ # a proper way.
174
+ #
175
+ # * nullify: It doesn't dup the field, it empties it. It's useful for
176
+ # objects we don't want to duplicate, like images in S3 (it can
177
+ # raise a timeout exception when duplicating).
178
+ #
179
+ # * link: It doesn't dup the field but stores the same object. It's
180
+ # useful in Active Record fields so we can store the same id and
181
+ # not creating a duplicate of the object (e.g. if we have a block
182
+ # with a related post we don't want the post to be duplicated)
183
+ def duplicate_field field
184
+ field_type = field_type field
185
+ field_value = read_field(field)
186
+
187
+ dupped_value = case layout_config.field(field)[:duplicate]
188
+ # When dupping we just dp the object and expect it has the right
189
+ # behaviour. If it's nil we save nil (you can't dup NilClass)
190
+ when :dup
191
+ field_value.nil? ? nil : field_value.dup
192
+ # When nullifying we return nil
193
+ when :nullify
194
+ nil
195
+ when :link
196
+ field_value
197
+ end
198
+
199
+ if field_type.is_a?(Class) && field_type < ActiveRecord::Base
200
+ # We save in the objects cache the dupped object
201
+ @cached_objects[field.to_sym] = dupped_value
202
+ # and then we store the new id in the fields_info hash
203
+ fields_info["#{field}_id".to_sym] =
204
+ dupped_value.nil? ? nil : dupped_value.id
205
+ else
206
+ # else we just dup it and save it into fields_info.
207
+ fields_info[field.to_sym] = dupped_value
208
+ end
209
+ end
210
+
211
+ ##
212
+ # Saves every related object from the objects cache
213
+ def save_related_objects
214
+ # Now we save each activerecord related object
215
+ @cached_objects.each do |field, object|
216
+ # Notice that we don't care if the object is actually saved.
217
+ #
218
+ # We don't care because there may be some cases where no real
219
+ # information is sent to an object but something is sent (i.e. the
220
+ # locale in a new Globalize translation) and then the object is
221
+ # created empty.
222
+ #
223
+ # When this happens if we save! the object an error is thrown and
224
+ # we can't leave the object blank
225
+ if object.is_a?(ActiveRecord::Base) && object.save
226
+ fields_info["#{field}_id".to_sym] = object.id
227
+ end
228
+ end
229
+ end
230
+
231
+ ##
232
+ # In this missing method we check wether we're asking for one field
233
+ # in which case we will read or write it.
234
+ #
235
+ # If there's no field we just let it go to super.
236
+ def method_missing(m, *args, &block)
237
+ # We get the name of the field stripping out the '=' for writers
238
+ field = m.to_s
239
+ write_accessor = field.ends_with? '='
240
+ field.gsub!(/\=$/, '')
241
+
242
+
243
+ # If we don't have this field then we send it to super and pry
244
+ if field == 'layout' || !has_field?(field)
245
+ super
246
+ # If this field exists, and it's not translated, then we do whatever
247
+ # we need to do
248
+ elsif !layout_config.field(field)[:translated]
249
+ write_accessor ?
250
+ write_field(field, args.first) :
251
+ read_field(field.to_sym)
252
+
253
+ # If it's translated but we are not in the translation (we check
254
+ # this by checking if we have translations) then we use the
255
+ # default translation to obtain it
256
+ elsif !self.is_translation? &&
257
+ layout_config.field(field)[:translated]
258
+
259
+ # When we are creating the block we still have no translation
260
+ # and we need to fill the layout. Otherwise no write or read
261
+ # field will work
262
+ translation.layout = self.layout
263
+
264
+ write_accessor ?
265
+ translation.write_field(field, args.first) :
266
+ translation.read_field(field.to_sym)
267
+ end
268
+ end
269
+
270
+ ##
271
+ # When we are assigning attributes (this method is called in new,
272
+ # create...) we must split those fields from our current layout and
273
+ # those who are not (they must be attributes).
274
+ #
275
+ # Attributes are processed the usual way and fields are written later
276
+ def assign_attributes new_attributes
277
+ fields = []
278
+
279
+ set_blank_fields
280
+
281
+ # We get the layout
282
+ new_layout = new_attributes[:layout] || new_attributes['layout']
283
+ self.layout = new_layout unless new_layout.nil?
284
+
285
+ Rails.logger.info "Searching #{new_attributes.keys.inspect} fields"+
286
+ "in #{self.layout} layout"
287
+
288
+ # And now separate fields and attributes
289
+ fields = new_attributes.select{|k, _| has_field? k }.symbolize_keys
290
+ # Now we filter those fields we must not manage because we are (or
291
+ # not) in a translation. I.e: if we have a translated field, but we
292
+ # are not in a translation then we let the translated field go and
293
+ # not manage it here
294
+ fields.select!{|k, _| layout_config.field(k)[:translated] == is_translation? }
295
+
296
+ # We purge the fields from the attributes
297
+ new_attributes.reject!{|k, _| fields.has_key? k }
298
+
299
+ # If we have translations we're going to need the layout in their
300
+ # attributes too so they can validate the fields.
301
+ # This is actually only neccesary when creating the translations,
302
+ # but we can afford to send the layout always to the translations
303
+ if new_attributes.has_key? :translations_attributes
304
+ new_attributes[:translations_attributes].each do |translation|
305
+ translation[:layout] = self.layout
306
+ end
307
+ end
308
+
309
+ super(new_attributes)
310
+
311
+ Rails.logger.info "Writing #{fields.inspect} to #{self.layout} block"
312
+
313
+ fields.each do |field_name, value|
314
+ self.write_field field_name, value
315
+ end
316
+ end
317
+
318
+ ##
319
+ # It cleans the objects cache and executes the default behaviour.
320
+ def reload *args
321
+ @cached_objects = {}
322
+ super
323
+ end
324
+
325
+ ##
326
+ # Overwriting duplication method. This method relies on super for any
327
+ # default behaviour, then duplicate the fields just as their
328
+ # configuration demands and then allow the object to custimize the
329
+ # duplication through the duplicate_self method.
330
+ def dup
331
+ new_self = super
332
+
333
+ # Now we recover all the fields that must be duplicated here
334
+ fields_to_duplicate.keys.each do |field_to_duplicate|
335
+ new_self.duplicate_field field_to_duplicate
336
+ end
337
+
338
+ # And allow the class itself to append some behaviour
339
+ duplicate_self new_self
340
+
341
+ new_self
342
+ end
343
+
344
+ ##
345
+ # This method allows us to introduce some custom duplication for each
346
+ # class that includes the concern.
347
+ #
348
+ # Basic behaviour is... doing nothing
349
+ def duplicate_self new_self
350
+ end
351
+
352
+ ##
353
+ # This method returns a list of the fields to duplicate in this
354
+ # object. In the concern we will use all the fields and if the classes
355
+ # need to modify it they will.
356
+ #
357
+ # Notice that if the behaviour is that when duplicating the field is
358
+ # nullfied we will return the field here and the duplicate_field will
359
+ # manage it properly.
360
+ def fields_to_duplicate
361
+ fields_configuration
362
+ end
363
+
364
+ ##
365
+ # When dupping we need to overwrite the cached_objects attribute.
366
+ # Otherwise the cached_object from the original object would start to
367
+ # be populated with the objects of the dupped one.
368
+ def initialize_dup(other)
369
+ @cached_objects = {}
370
+ super
371
+ end
372
+
373
+ private
374
+
375
+ ##
376
+ # Initializes both the fields_info hash and the objects cache.
377
+ def set_blank_fields
378
+ @fields_info ||= {}
379
+ @cached_objects ||= {}
380
+ end
381
+
382
+ end
383
+ end
384
+ end
385
+ end
386
+ end