nocms-blocks 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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