contents_core 0.2.5 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75a1c3e706d549e2a1760b2986ab6ed1c947e62c
4
- data.tar.gz: 50c1f08869d686fb75509b3d26a92c8b59873196
3
+ metadata.gz: 33b635a47b2e5a090eea2d2c99dba30eef64c508
4
+ data.tar.gz: 93e8aa348c86bc228f0b0572a7fbdbec58fe5ad4
5
5
  SHA512:
6
- metadata.gz: 9ac52395df24233ceda982dceab0871716058adf72bfd99cb5fbb98b67ce519b7830186c37b68441a7542b7a11658a0127c9ea5f7084a4bf0875228a4fafe9e2
7
- data.tar.gz: 92ccd52237adcb6bf89352b9d6d583984daac7c54aadb3659268efdca2dfe298eff598c24a0e6ca0545afa9f0286e75dbae82e6817fdf1a25fe63caa63bdd9fb
6
+ metadata.gz: cb9f8914cbd38b7d91ae103c317c8cbc6310153f2a35d9b294d98c5abfcc84ee919cbbd406e89590ab935bf23cc82dab254e4ed315734c63350957cd0887594e
7
+ data.tar.gz: 3b341cdfe78f537cf7118fedc018d49bf834d4677fd1358cecdc2522bef8747411e951e92adee917df5556cadc04c8fb294ef0d31e35d5104513484e2fe74db5
@@ -1,4 +1,4 @@
1
- Copyright 2017
1
+ Copyright 2017, Mattia Roccoberton
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  # ContentsCore [![Gem Version](https://badge.fury.io/rb/contents_core.svg)](https://badge.fury.io/rb/contents_core) [![Build Status](https://travis-ci.org/blocknotes/contents_core.svg)](https://travis-ci.org/blocknotes/contents_core)
2
2
 
3
- A Rails gem which offer a simple structure to manage contents in a flexible way.
3
+ A Rails gem which offer a structure to manage contents in a flexible way: blocks with recursive nested blocks + items as "leaves"
4
4
 
5
5
  Disclaimer: this component is in ALPHA, major changes could happen
6
6
 
7
7
  Goals:
8
8
  - attach the contents structure to a model transparently
9
- - improve block views management
10
9
  - add fields to blocks without migrations
10
+ - offer helpers to render blocks in views
11
+ - cache-ready
11
12
 
12
- ### Install
13
+ ## Install
13
14
 
14
15
  - Add to the Gemfile:
15
16
  `gem 'contents_core'`
@@ -17,24 +18,85 @@ Goals:
17
18
  `rails contents_core:install:migrations`
18
19
  - Execute migrations
19
20
  - Add the concern *Blocks* to your model (ex. *Page*): `include ContentsCore::Blocks`
20
- - Add the blocks to a view (ex. *page show*): `= render partial: 'contents_core/blocks', locals: { container: @page }`
21
+ - Optionally add the blocks to a view (ex. *page show*): `= render partial: 'contents_core/blocks', locals: { container: @page }`
21
22
 
22
- ### Config
23
+ ## Usage
24
+
25
+ ### Working with blocks/items
26
+
27
+ - Basic operations (example parent model: *Page*):
28
+ ```ruby
29
+ page = Page.first
30
+ page.create_block :slider, name: 'a-slider', create_children: 3 # Create a silder with 3 slides
31
+ page.current_blocks.map{ |block| block.name } # current_blocks -> all published ordered blocks and query cached
32
+ block = page.get_block 'a-slider'
33
+ block.tree # list all items of a block
34
+ block.get 'slide-2.title' # get value of 'title' field of sub block with name 'slide-2' (name automatically generated at creation)
35
+ block.set 'slide-2.title' # set field value
36
+ block.save
37
+ ```
38
+
39
+ - Other operations:
40
+ ```ruby
41
+ block = ContentsCore::Block.last
42
+ ContentsCore.create_block_in_parent block, :text # create a sub block in a block
43
+ block.create_item :item_string, name: 'a-field'
44
+ ```
45
+
46
+ ## Config
23
47
 
24
48
  Edit the conf file: `config/initializers/contents_core.rb`
25
49
 
26
50
  ```ruby
27
- conf = ContentsCore.config
28
- # Adds a new custom block
29
- conf[:cc_blocks][:custom] = {
30
- name: 'Custom block',
31
- items: {
32
- title: :item_string,
33
- content: :item_text,
34
- image: :item_file
51
+ module ContentsCore
52
+ @@config = {
53
+ blocks: {
54
+ text: {
55
+ name: :text_only, # used as reference / for translations
56
+ children: { # children: sub blocks & items
57
+ title: :item_string,
58
+ content: :item_text
59
+ }
60
+ },
61
+ image: {
62
+ name: :image_only,
63
+ children: {
64
+ img: :item_file
65
+ }
66
+ },
67
+ slide: {
68
+ name: :a_slide,
69
+ child_only: true, # used only as child of another block (slider)
70
+ children: {
71
+ img: :item_file,
72
+ link: :item_string,
73
+ title: :item_string
74
+ }
75
+ },
76
+ slider: {
77
+ name: :a_slider,
78
+ new_children: :slide, # block type used when creating a new child with default params
79
+ children: {
80
+ slide: :slide
81
+ }
82
+ },
83
+ },
84
+ items: {
85
+ item_boolean: {},
86
+ item_datetime: {},
87
+ item_float: {},
88
+ item_hash: {},
89
+ item_file: {
90
+ input: :file_image
91
+ },
92
+ item_integer: {},
93
+ item_string: {},
94
+ item_text: {
95
+ input: :html
96
+ },
97
+ }
35
98
  }
36
- }
37
- ContentsCore.config( { components: conf[:cc_blocks] } )
99
+ end
38
100
  ```
39
101
 
40
102
  Create the new view blocks: `app/views/contents_core/_block_custom.html.slim`
@@ -46,7 +108,7 @@ Create the new view blocks: `app/views/contents_core/_block_custom.html.slim`
46
108
  .image = image_tag block.get( 'image' ).url( :thumb )
47
109
  ```
48
110
 
49
- #### Images
111
+ ### Images
50
112
 
51
113
  To add support for images add CarrierWave gem to your Gemfile and execute: `rails generate uploader Image` and update che config file *config/initializers/contents_core.rb* with:
52
114
 
@@ -84,36 +146,36 @@ module ContentsCore
84
146
  end
85
147
  ```
86
148
 
87
- #### Customizations
149
+ ## Customizations
88
150
 
89
151
  To create a "free form" block just use: `Page.first.create_block :intro, name: 'IntroBlock', schema: { intro: :item_string, subtitle: :item_string }`
90
152
 
91
153
  Then create a *app/view/contents_core/_block_intro* view.
92
154
 
93
- To list the blocks of a page: `Page.first.cc_blocks.pluck :name`
155
+ To list the blocks of a page manually (but *current_blocks* method is the preferred way): `Page.first.cc_blocks.pluck :name`
94
156
 
95
157
  To add a new field to an existing block (ex. to first Page, on the first Block):
96
158
 
97
159
  ```rb
98
160
  block = Page.first.get_block 'text-1'
99
- block.create_item( 'ContentsCore::ItemString', 'new-field' ).set( 'A test...' ).save
161
+ block.create_item( :item_string, name: 'new-field' ).set( 'A test...' ).save
100
162
  ```
101
163
 
102
164
  Then add to the block view: `block.get( 'new-field' )`
103
165
 
104
166
  To set a field value: `block.set( 'new-field', 'Some value' )`
105
167
 
106
- #### ActiveAdmin
168
+ ### ActiveAdmin
107
169
 
108
170
  If you use ActiveAdmin as admin interface you can find a sample model configuration: [page](extra/active_admin_page.rb) plus a js [page](extra/active_admin.js)
109
171
 
110
- ### Notes
172
+ ## Notes
111
173
 
112
- - Blocks enum: `ContentsCore::Block.block_enum`
113
- - Blocks types: `ContentsCore::Block.block_types`
174
+ - Blocks enum: `ContentsCore::Block.enum`
175
+ - Blocks types: `ContentsCore::Block.types`
114
176
  - Default blocks [here](config/initializers/contents_core.rb)
115
177
 
116
- #### Structure
178
+ ### Structure
117
179
 
118
180
  - Including the Blocks concern to a model will add `has_many :cc_blocks` relationship (the list of blocks attached to a container) and some utility methods
119
181
  - Block: UI component, a group of items (ex. a text with a title, a slider, a 3 column text widget, etc.); built with a list of sub blocks (for nested components) and a list of items
data/Rakefile CHANGED
@@ -21,7 +21,6 @@ load 'rails/tasks/engine.rake'
21
21
  load 'rails/tasks/statistics.rake'
22
22
 
23
23
 
24
-
25
24
  require 'bundler/gem_tasks'
26
25
 
27
26
  require 'rake/testtask'
@@ -1,33 +1,35 @@
1
1
  module ContentsCore
2
2
  class Block < ApplicationRecord
3
- EMPTY_DATA = OpenStruct.new( { data: '' } )
3
+ # --- constants -----------------------------------------------------------
4
+ # EMPTY_DATA = OpenStruct.new( { data: '' } )
4
5
 
6
+ # --- misc ----------------------------------------------------------------
5
7
  attr_accessor :create_children
8
+ serialize :conf, Hash
6
9
 
7
- # --- fields options ----------------------------------------------------- #
8
- serialize :options, JSON
9
- serialize :validations, JSON
10
-
11
- # --- relations ---------------------------------------------------------- #
12
- belongs_to :parent, polymorphic: true
10
+ # --- associations --------------------------------------------------------
11
+ belongs_to :parent, polymorphic: true, touch: true
13
12
  has_many :cc_blocks, as: :parent, dependent: :destroy, foreign_key: 'parent_id', class_name: 'Block'
14
13
  has_many :items, dependent: :destroy
15
14
  accepts_nested_attributes_for :cc_blocks, allow_destroy: true
16
15
  accepts_nested_attributes_for :items
17
16
 
18
- # --- hooks -------------------------------------------------------------- #
17
+ # --- callbacks -----------------------------------------------------------
18
+ # after_initialize :on_after_initialize
19
19
  before_create :on_before_create
20
20
  after_create :on_after_create
21
21
 
22
- # --- scopes ------------------------------------------------------------- #
22
+ # --- scopes --------------------------------------------------------------
23
23
  default_scope { order( :position ) }
24
24
  scope :published, -> { where( published: true ) unless ContentsCore.editing }
25
25
  scope :with_nested, -> { includes( :items, cc_blocks: :items ) }
26
26
 
27
- # --- validations -------------------------------------------------------- #
27
+ # --- validations ---------------------------------------------------------
28
28
  validates_presence_of :block_type, :position
29
+ validates_associated :cc_blocks
30
+ validates_associated :items
29
31
 
30
- # --- misc --------------------------------------------------------------- #
32
+ # --- tmp -----------------------------------------------------------------
31
33
  ## amoeba do
32
34
  ## enable
33
35
  ## # customize( lambda { |original_obj, new_obj|
@@ -53,11 +55,13 @@ module ContentsCore
53
55
  #
54
56
  # # scope :published, -> { where( published: true ) unless ApplicationController.edit_mode }
55
57
 
58
+ # --- methods -------------------------------------------------------------
56
59
  def initialize( attributes = {}, &block )
57
60
  super( attributes, &block )
58
- @create_children = 1
61
+ @create_children = 0
62
+ self.conf = {} unless self.conf
59
63
  self.group = config[:group]
60
- self.block_type = parent.config[:children_type] if attributes[:block_type].nil? && self.parent_type == 'ContentsCore::Block'
64
+ self.block_type = parent.config[:new_children] if attributes[:block_type].nil? && self.parent_type == 'ContentsCore::Block'
61
65
  end
62
66
 
63
67
  def as_json( options = nil )
@@ -68,19 +72,21 @@ module ContentsCore
68
72
  "#{self.class.to_s.split('::').last}-#{self.id}"
69
73
  end
70
74
 
71
- def children_type
72
- config[:children_type]
73
- end
74
-
75
75
  def config
76
- ContentsCore.config[:cc_blocks][block_type.to_sym] ? ContentsCore.config[:cc_blocks][block_type.to_sym] : {}
77
- end
78
-
79
- def create_item( item_type, item_name = nil )
80
- new_item = ContentsCore::Item.new( type: item_type )
81
- new_item.name = item_name if item_name
82
- self.items << new_item
83
- new_item
76
+ !self.conf.blank? ? self.conf : ( ContentsCore.config[:blocks][block_type.to_sym] ? ContentsCore.config[:blocks][block_type.to_sym] : {} )
77
+ end
78
+
79
+ def create_item( item_type, options = {} )
80
+ if ContentsCore.config[:items].keys.include? item_type
81
+ attrs = { type: "ContentsCore::#{item_type.to_s.classify}" } # TODO: check if model exists
82
+ attrs[:name] = options[:name] if options[:name]
83
+ attrs[:data] = options[:value] if options[:value]
84
+ item = self.items.new attrs
85
+ item.save
86
+ item
87
+ else
88
+ raise "Invalid item type: #{item_type} - check defined items in config"
89
+ end
84
90
  end
85
91
 
86
92
  def editable
@@ -94,25 +100,27 @@ module ContentsCore
94
100
  } :
95
101
  {
96
102
  'data-ec-block': self.id,
97
- 'data-ec-container': self.children_type,
103
+ 'data-ec-container': self.new_children,
98
104
  'data-ec-ct': self.block_type,
99
105
  'data-ec-pub': self.published
100
106
  }
101
107
  ).map { |k, v| "#{k}=\"#{v}\"" }.join( ' ' ).html_safe : ''
102
108
  end
103
109
 
104
- # Returns an item by name
110
+ # Returns an item value by name
105
111
  def get( name )
106
112
  item = get_item( name )
107
- item.data if item
113
+ item && item.is_a?( Item ) ? item.data : nil
108
114
  end
109
115
 
116
+ # Returns an item by name
110
117
  def get_item( name )
111
- unless @_items
112
- @_items = {}
113
- items.each { |item| @_items[item.name] = item }
118
+ t = tree
119
+ name.split( '.' ).each do |tok|
120
+ return nil unless t[tok]
121
+ t = t[tok]
114
122
  end
115
- @_items[name]
123
+ t
116
124
  end
117
125
 
118
126
  def has_parent?
@@ -124,21 +132,30 @@ module ContentsCore
124
132
  end
125
133
 
126
134
  def is_sub_block?
127
- parent.present? && parent_type == 'ContentsCore::Block'
135
+ self.parent.present? && self.parent.is_a?( Block )
136
+ end
137
+
138
+ def new_children
139
+ config[:new_children]
128
140
  end
129
141
 
130
142
  def on_after_create
131
143
  # TODO: validates type before creation!
132
- Block::init_items( self, config[:items] ) if Block::block_types( false ).include?( self.block_type.to_sym )
144
+ Block.initialize_children( self, config[:children] ) if Block.types( false ).include?( self.block_type.to_sym )
133
145
  end
134
146
 
147
+ # def on_after_initialize
148
+ # self.conf = {} unless self.conf
149
+ # end
150
+
135
151
  def on_before_create
136
- if self.name.blank?
137
- names = parent.cc_blocks.map( &:name )
152
+ names = parent.cc_blocks.map( &:name )
153
+ if self.name.blank? || names.include?( self.name ) # Search a not used name
154
+ n = self.name.blank? ? block_type : self.name
138
155
  i = 0
139
- while( ( i += 1 ) < 1000 ) # Search an empty group
140
- unless names.include? "#{block_type}-#{i}"
141
- self.name = "#{block_type}-#{i}"
156
+ while( ( i += 1 ) < 1000 )
157
+ unless names.include? "#{n}-#{i}"
158
+ self.name = "#{n}-#{i}"
142
159
  break
143
160
  end
144
161
  end
@@ -148,14 +165,15 @@ module ContentsCore
148
165
  def props
149
166
  pieces = {}
150
167
 
151
- Item::item_types.each do |type|
152
- pieces[type.pluralize.to_sym] = []
168
+ Item.types.each do |type|
169
+ pieces[type.to_s.pluralize.to_sym] = []
153
170
  end
154
171
  items.each do |item| # TODO: improve me
172
+ pieces[item.class.type_name.pluralize.to_sym] = [] unless pieces[item.class.type_name.pluralize.to_sym]
155
173
  pieces[item.class.type_name.pluralize.to_sym].push item
156
174
  end
157
- Item::item_types.each do |type|
158
- pieces[type.to_sym] = pieces[type.pluralize.to_sym].any? ? pieces[type.pluralize.to_sym].first : nil # EMPTY_DATA - empty Item per sti class?
175
+ Item.types.each do |type|
176
+ pieces[type] = pieces[type.pluralize.to_sym].any? ? pieces[type.pluralize.to_sym].first : nil # EMPTY_DATA - empty Item per sti class?
159
177
  end
160
178
 
161
179
  # pieces = {
@@ -174,44 +192,62 @@ module ContentsCore
174
192
  end
175
193
 
176
194
  def set( name, value )
177
- items.each do |item|
178
- if item.name == name
179
- item.data = value
180
- break
181
- end
182
- end
195
+ item = get_item( name )
196
+ item && item.is_a?( Item ) ? item.data = value : nil
197
+ end
198
+
199
+ def tree
200
+ # return @items_tree if @items_tree
201
+ @items_tree = {} # prepare a complete list of items
202
+ self.items.each{ |item| @items_tree[item.name] = item }
203
+ self.cc_blocks.each_with_index{ |block, i| @items_tree[block.name] = block.tree } # @items_tree[i] = block.tree
204
+ @items_tree
183
205
  end
184
206
 
185
- def self.block_enum( include_children = true )
186
- ContentsCore.config[:cc_blocks].map{|k, v| [v[:name], k.to_s] if !include_children || !v[:child_only]}.compact.sort_by{|b| b[0]}
207
+ def validations
208
+ config[:validations] || {}
187
209
  end
188
210
 
189
- def self.block_types( include_children = true )
190
- ContentsCore.config[:cc_blocks].select{|k, v| !include_children || !v[:child_only]}.keys
211
+ def self.enum( include_children = true )
212
+ ContentsCore.config[:blocks].map{|k, v| [I18n.t( 'contents_core.blocks.' + v[:name].to_s ), k.to_s] if !include_children || !v[:child_only]}.compact.sort_by{|b| b[0]}
191
213
  end
192
214
 
193
- def self.init_items( block, items, options = {} )
194
- items.each do |name, type|
215
+ def self.initialize_children( block, children, options = {} )
216
+ children.each do |name, type|
195
217
  t = type.to_sym
196
- if type.to_s.start_with? 'item_'
218
+ if Item.types.include? t
197
219
  c = 'ContentsCore::' + ActiveSupport::Inflector.camelize( t )
198
220
  begin
199
221
  model = c.constantize
200
222
  rescue Exception => e
201
- Rails.logger.error '[ERROR] ContentsCore - init_items: ' + e.message
223
+ Rails.logger.error '[ERROR] ContentsCore - initialize_children: ' + e.message
202
224
  model = false
203
225
  end
204
- block.items << model.new( name: name ).init if model
205
- elsif Block::block_types( false ).include? t.to_sym
226
+ if model
227
+ block.items.new( type: model.name, name: name )
228
+ end
229
+ elsif Block.types( false ).include? t
206
230
  block.create_children.times do
207
- block.cc_blocks << Block.new( block_type: t, name: name )
231
+ block.cc_blocks.new( block_type: t, name: name )
232
+ block.save
208
233
  end
209
234
  end
210
- end if items
235
+ end if children
236
+ block.save
237
+ end
238
+
239
+ def self.items_keys( keys )
240
+ keys.map do |k, v|
241
+ v.is_a?( Hash ) ? items_keys( v ) : k
242
+ end.flatten
211
243
  end
212
244
 
213
245
  def self.permitted_attributes
214
246
  [ :id, :name, :block_type, :position, :_destroy, items_attributes: [ :id ] + Item::permitted_attributes, cc_blocks_attributes: [ :id, :name, :block_type, items_attributes: [ :id ] + Item::permitted_attributes ] ]
215
247
  end
248
+
249
+ def self.types( include_children = true )
250
+ @@types ||= ContentsCore.config[:blocks].select{|k, v| !include_children || !v[:child_only]}.keys.map( &:to_sym )
251
+ end
216
252
  end
217
253
  end
@@ -1,10 +1,25 @@
1
1
  module ContentsCore
2
2
  class Item < ApplicationRecord
3
- # field :data, type: String
3
+ # --- associations --------------------------------------------------------
4
+ belongs_to :block, touch: true
5
+
6
+ # --- callbacks -----------------------------------------------------------
7
+ after_initialize :on_after_initialize
8
+ before_create :on_before_create
4
9
 
10
+ # --- misc ----------------------------------------------------------------
11
+ # field :data, type: String
5
12
  # embedded_in :cc_blocks
6
13
 
7
- belongs_to :block
14
+ # --- validations ---------------------------------------------------------
15
+ validate :validate_item
16
+ validates :block, presence: true, allow_blank: false
17
+
18
+ # --- methods -------------------------------------------------------------
19
+ def on_after_initialize
20
+ self.data = config[:default] if config[:default] && !self.data
21
+ self.init
22
+ end
8
23
 
9
24
  def as_json( options = nil )
10
25
  super( {only: [:id, :name, :type], methods: [:data]}.merge(options || {}) )
@@ -15,16 +30,44 @@ module ContentsCore
15
30
  end
16
31
 
17
32
  def class_name
18
- self.class.to_s.split('::').last
33
+ self.class.to_s.split( '::' ).last
34
+ end
35
+
36
+ def config
37
+ @config ||= ( ContentsCore.config[:items] && ContentsCore.config[:items][self.class_name.underscore.to_sym] ? ContentsCore.config[:items][self.class_name.underscore.to_sym] : {} ).merge( self.block && self.block.config[:options] && self.name && self.block.config[:options][self.name.to_sym] ? self.block.config[:options][self.name.to_sym] : {} )
38
+ end
39
+
40
+ def data_type
41
+ @data_type ||= ( config[:data_type] || :string ).to_sym
19
42
  end
20
43
 
21
44
  def editable
22
45
  ContentsCore.editing ? " data-ec-item=\"#{self.id}\" data-ec-input=\"#{self.opt_input}\" data-ec-type=\"#{self.class_name}\"".html_safe : ''
23
46
  end
24
47
 
48
+ def init # placeholder method (for override)
49
+ end
50
+
51
+ def on_before_create
52
+ # root_block = self.block
53
+ # root_block = root_block.parent while root_block.parent.is_a? Block
54
+ # names = Block.items_keys root_block.tree
55
+ names = ( self.block.items - [self] ).pluck :name
56
+ if self.name.blank? || names.include?( self.name ) # Search a not used name
57
+ n = self.name.blank? ? self.class.type_name : self.name
58
+ i = 0
59
+ while( ( i += 1 ) < 1000 )
60
+ unless names.include? "#{n}-#{i}"
61
+ self.name = "#{n}-#{i}"
62
+ break
63
+ end
64
+ end
65
+ end
66
+ end
67
+
25
68
  def opt_input
26
- if self.block.options[self.name] && self.block.options[self.name]['input']
27
- self.block.options[self.name]['input'].to_s
69
+ if self.block.config[self.name] && self.block.config[self.name]['input']
70
+ self.block.config[self.name]['input'].to_s
28
71
  elsif config[:input]
29
72
  config[:input].to_s
30
73
  else
@@ -50,20 +93,20 @@ module ContentsCore
50
93
  self.save
51
94
  end
52
95
 
53
- def self.item_types
54
- @@item_types ||= ContentsCore.config[:items].keys.map( &:to_s )
96
+ def validate_item
97
+ config[:validate].call( self ) if config[:validate]
55
98
  end
56
99
 
57
100
  def self.permitted_attributes
58
101
  [ :data_boolean, :data_datetime, :data_file, :data_float, :data_hash, :data_integer, :data_string, :data_text ]
59
102
  end
60
103
 
61
- def config
62
- @config ||= self.block && self.block.config[:options] && self.block.config[:options][self.name.to_sym] ? self.block.config[:options][self.name.to_sym] : ( ContentsCore.config[:items][self.class::type_name.to_sym] ? ContentsCore.config[:items][self.class::type_name.to_sym] : {} )
104
+ def self.type_name
105
+ ''
63
106
  end
64
107
 
65
- def data_type
66
- @data_type ||= ( config[:data_type] || :string ).to_sym
108
+ def self.types
109
+ @@types ||= ContentsCore.config[:items].keys.map( &:to_sym )
67
110
  end
68
111
 
69
112
  protected
@@ -88,7 +131,7 @@ module ContentsCore
88
131
  end
89
132
  end
90
133
 
91
- def converted_data()
134
+ def converted_data
92
135
  if data_type
93
136
  case data_type
94
137
  when :boolean
@@ -43,7 +43,11 @@ module ContentsCore
43
43
  end
44
44
 
45
45
  def init
46
- self.data_string = []
46
+ unless self.data
47
+ self.data_string = []
48
+ self.data_hash = []
49
+ self.data_text = []
50
+ end
47
51
  self
48
52
  end
49
53
 
@@ -3,7 +3,7 @@ module ContentsCore
3
3
  alias_attribute :data, :data_boolean
4
4
 
5
5
  def init
6
- self.data = 0
6
+ self.data = false unless self.data
7
7
  self
8
8
  end
9
9
 
@@ -3,7 +3,7 @@ module ContentsCore
3
3
  alias_attribute :data, :data_datetime
4
4
 
5
5
  def init
6
- self.data = Time.now
6
+ self.data = Time.now unless self.data
7
7
  self
8
8
  end
9
9
 
@@ -16,7 +16,7 @@ module ContentsCore
16
16
  end
17
17
 
18
18
  def init
19
- # self.data_file = File.open( ContentsCore::Engine.root.join( 'lib', 'data', 'img1.jpg' ) )
19
+ # self.data_file = File.open( ContentsCore::Engine.root.join( 'lib', 'data', 'missing.jpg' ) ) unless self.data
20
20
  self
21
21
  end
22
22
 
@@ -31,7 +31,7 @@ module ContentsCore
31
31
  # before_validation :on_before_validation
32
32
  #
33
33
  # def on_before_validation
34
- # self.data = File.open( 'rails/editable_components_test/public/img/img1.jpg' )
34
+ # self.data = File.open( 'test/public/img/img1.jpg' )
35
35
  # # binding.pry
36
36
  # end
37
37
 
@@ -3,7 +3,7 @@ module ContentsCore
3
3
  alias_attribute :data, :data_float
4
4
 
5
5
  def init
6
- self.data = 0
6
+ self.data = 0 unless self.data
7
7
  self
8
8
  end
9
9
 
@@ -5,7 +5,7 @@ module ContentsCore
5
5
  serialize :data_hash, Hash
6
6
 
7
7
  def init
8
- self.data = {}
8
+ self.data = {} unless self.data
9
9
  self
10
10
  end
11
11
 
@@ -3,7 +3,7 @@ module ContentsCore
3
3
  alias_attribute :data, :data_integer
4
4
 
5
5
  def init
6
- self.data = 0
6
+ self.data = 0 unless self.data
7
7
  self
8
8
  end
9
9
 
@@ -1,12 +1,18 @@
1
1
  # TODO: needs improvements
2
2
  module ContentsCore
3
3
  class ItemObject < Item
4
- alias_attribute :data, :data_hash
5
-
6
4
  serialize :data_hash, JSON
7
5
 
6
+ def data
7
+ self.data_hash.deep_symbolize_keys
8
+ end
9
+
10
+ def data=( value )
11
+ self.data_hash = value
12
+ end
13
+
8
14
  def init
9
- self.data = {}
15
+ self.data = {} unless self.data
10
16
  self
11
17
  end
12
18
 
@@ -2,20 +2,20 @@ module ContentsCore
2
2
  class ItemString < Item
3
3
  alias_attribute :data, :data_string
4
4
 
5
- validate :on_validate
5
+ # validate :on_validate
6
6
 
7
7
  def init
8
- self.data = self.name.gsub( /-/, ' ' ).humanize
8
+ self.data = '' unless self.data # self.name.gsub( /-/, ' ' ).humanize
9
9
  self
10
10
  end
11
11
 
12
- def on_validate
13
- if self.block.validations[self.name]
14
- if self.block.validations[self.name] == 'required'
15
- self.errors.add( :base, "#{self.name} is required" ) if self.data_string.blank?
16
- end
17
- end
18
- end
12
+ # def on_validate
13
+ # if self.block.validations[self.name]
14
+ # if self.block.validations[self.name] == 'required'
15
+ # self.errors.add( :base, "#{self.name} is required" ) if self.data_string.blank?
16
+ # end
17
+ # end
18
+ # end
19
19
 
20
20
  def update_data( value )
21
21
  self.data = ActionController::Base.helpers.sanitize( value, tags: [] )
@@ -3,7 +3,7 @@ module ContentsCore
3
3
  alias_attribute :data, :data_text
4
4
 
5
5
  def init
6
- self.data = self.name.gsub( /-/, ' ' ).humanize
6
+ self.data = '' unless self.data # self.name.gsub( /-/, ' ' ).humanize
7
7
  self
8
8
  end
9
9
 
@@ -2,44 +2,49 @@ module ContentsCore
2
2
  @@editing = false
3
3
 
4
4
  @@config = {
5
- cc_blocks: {
5
+ blocks: {
6
6
  image: {
7
7
  name: 'Image block',
8
- items: {
8
+ children: {
9
9
  img: :item_file
10
10
  }
11
11
  },
12
12
  multi_text: {
13
- children_type: :text,
14
13
  name: 'Multi columns block',
15
- items: {
14
+ new_children: :text,
15
+ children: {
16
16
  column: :text
17
- }
17
+ },
18
18
  },
19
19
  slide: {
20
20
  name: 'Slide block',
21
- items: {
21
+ children: {
22
22
  img: :item_file,
23
23
  title: :item_string
24
24
  }
25
25
  },
26
26
  slider: {
27
- children_type: :slide,
28
27
  name: 'Slider block',
29
- items: {
28
+ new_children: :slide,
29
+ children: {
30
30
  slide: :slide
31
31
  }
32
32
  },
33
33
  text: {
34
34
  name: 'Text block',
35
- items: {
35
+ children: {
36
36
  title: :item_string,
37
37
  content: :item_text
38
- }
38
+ },
39
+ # options: {
40
+ # title: {
41
+ # validate: ->( item ) { item.errors.add( :data_string, "can't be blank" ) if item.data.blank? }
42
+ # }
43
+ # }
39
44
  },
40
45
  text_with_image: {
41
46
  name: 'Text with image block',
42
- items: {
47
+ children: {
43
48
  img: :item_file,
44
49
  title: :item_string,
45
50
  content: :item_text
@@ -47,18 +52,18 @@ module ContentsCore
47
52
  },
48
53
  },
49
54
  items: {
50
- array: {},
51
- boolean: {},
52
- datetime: {},
53
- float: {},
54
- hash: {},
55
- file: {
55
+ item_array: {},
56
+ item_boolean: {},
57
+ item_datetime: {},
58
+ item_float: {},
59
+ item_hash: {},
60
+ item_file: {
56
61
  input: :file_image
57
62
  },
58
- integer: {},
59
- object: {},
60
- string: {},
61
- text: {
63
+ item_integer: {},
64
+ item_object: {},
65
+ item_string: {},
66
+ item_text: {
62
67
  input: :html
63
68
  },
64
69
  }
@@ -1,16 +1,16 @@
1
1
  class CreateContentsCoreBlocks < ActiveRecord::Migration[5.0]
2
2
  def change
3
3
  create_table :contents_core_blocks do |t|
4
- t.string :block_type, null: false, default: 'text'
5
- t.integer :version, null: false, default: 0
6
- t.string :name, null: false, default: ''
7
- t.string :group
8
- t.integer :position, null: false, default: 0
9
- t.boolean :published, null: false, default: true
10
- t.string :options, null: false, default: '{}'
11
- t.string :validations, null: false, default: '{}'
12
- t.integer :parent_id
13
- t.string :parent_type
4
+ t.string :block_type, null: false, default: 'text'
5
+ t.string :name
6
+ t.string :group
7
+ t.integer :version, null: false, default: 0
8
+ t.integer :position, null: false, default: 0
9
+ t.boolean :published, null: false, default: true
10
+ t.text :conf
11
+ t.integer :parent_id
12
+ t.string :parent_type
13
+ t.timestamps null: false
14
14
  end
15
15
 
16
16
  add_index :contents_core_blocks, [:parent_id, :parent_type]
@@ -1,9 +1,18 @@
1
1
  class CreateContentsCoreItems < ActiveRecord::Migration[5.0]
2
2
  def change
3
3
  create_table :contents_core_items do |t|
4
- t.string :type
5
- t.string :name, null: false, default: 'data'
6
- t.integer :block_id
4
+ t.string :type
5
+ t.string :name
6
+ t.integer :block_id
7
+ t.boolean :data_boolean
8
+ t.datetime :data_datetime
9
+ t.string :data_file # , null: false, default: ''
10
+ t.float :data_float
11
+ t.text :data_hash
12
+ t.integer :data_integer
13
+ t.string :data_string # , null: false, default: ''
14
+ t.text :data_text
15
+ t.timestamps null: false
7
16
  end
8
17
 
9
18
  add_index :contents_core_items, :block_id
@@ -10,11 +10,18 @@ module ContentsCore
10
10
  def self.create_block_in_parent( parent, type = :text, params = {} )
11
11
  block = Block.new( block_type: type )
12
12
  block.name = params[:name] if params[:name]
13
- block.options = params[:options] if params[:options]
14
- block.validations = params[:validations] if params[:validations]
13
+ block.conf = params[:conf] if params[:conf]
14
+ # block.validations = params[:validations] if params[:validations]
15
15
  block.create_children = params[:create_children].to_i if params[:create_children]
16
- parent.cc_blocks << block
17
- Block::init_items block, params[:schema], {create_children: params[:create_children]} if params[:schema]
16
+ parent.cc_blocks << block # TODO: change me (with cc_blocks.new)
17
+ Block::initialize_children block, params[:schema], {create_children: params[:create_children]} if params[:schema]
18
+ if params[:values]
19
+ traverse_hash block.tree, params[:values]
20
+ block.save
21
+ elsif params[:values_list]
22
+ params[:values_list].each{ |k, v| block.set k.to_s, v }
23
+ block.save
24
+ end
18
25
  block
19
26
  end
20
27
 
@@ -22,4 +29,25 @@ module ContentsCore
22
29
  @@editing = editing unless editing.nil?
23
30
  @@editing
24
31
  end
32
+
33
+ def self.traverse_hash( hash, values )
34
+ ret = {}
35
+ values.each do |k, v|
36
+ if v.is_a? Hash
37
+ ret = traverse_hash hash[k.to_s], values[k]
38
+ else
39
+ hash[k.to_s].data = values[k]
40
+ end
41
+ end
42
+ ret
43
+ end
44
+
45
+ # def self.parse_attr( attribute )
46
+ # attr = attribute.to_s
47
+ # if attr.include?( '.' )
48
+ # attrs = attr.split( '.' )
49
+ # attr = attrs.shift + attrs.map{|tok| "[#{tok}]"}.join
50
+ # end
51
+ # attr
52
+ # end
25
53
  end
@@ -12,9 +12,11 @@ module ContentsCore
12
12
  end
13
13
 
14
14
  def current_blocks( version = 0 )
15
- return @current_blocks if @current_blocks
15
+ # return @current_blocks if @current_blocks
16
16
  version = 0 unless ContentsCore.editing # no admin = only current version
17
- @current_blocks = cc_blocks.where( version: version.to_i ).with_nested.published
17
+ Rails.cache.fetch( "#{cache_key}/current_blocks/#{version}", expires_in: 12.hours ) do
18
+ self.cc_blocks.where( version: version.to_i ).with_nested.published
19
+ end
18
20
  end
19
21
 
20
22
  def get_block( name, version = 0 )
@@ -23,6 +25,12 @@ module ContentsCore
23
25
  end
24
26
  nil
25
27
  end
28
+
29
+ protected
30
+
31
+ def cache_key
32
+ self.cc_blocks.published.select( :updated_at ).order( updated_at: :desc ).first.try( :updated_at ).to_i
33
+ end
26
34
  end
27
35
  end
28
36
  end
@@ -1,3 +1,3 @@
1
1
  module ContentsCore
2
- VERSION = '0.2.5'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contents_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-02 00:00:00.000000000 Z
11
+ date: 2017-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,44 +16,58 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5'
19
+ version: '5.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5'
26
+ version: '5.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sqlite3
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '1.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.11'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.15'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '0'
55
- description: A Rails gem which offer a simple structure to manage contents in a flexible
56
- way
68
+ version: '0.15'
69
+ description: 'A Rails gem which offer a structure to manage contents in a flexible
70
+ way: blocks with recursive nested blocks + items as "leaves"'
57
71
  email:
58
72
  - mat@blocknot.es
59
73
  executables: []
@@ -89,7 +103,6 @@ files:
89
103
  - config/routes.rb
90
104
  - db/migrate/20170414173603_create_contents_core_blocks.rb
91
105
  - db/migrate/20170414173610_create_contents_core_items.rb
92
- - db/migrate/20170414173611_add_extra_items.rb
93
106
  - lib/contents_core.rb
94
107
  - lib/contents_core/blocks.rb
95
108
  - lib/contents_core/engine.rb
@@ -1,12 +0,0 @@
1
- class AddExtraItems < ActiveRecord::Migration[5.0]
2
- def change
3
- add_column :contents_core_items, :data_boolean, :boolean
4
- add_column :contents_core_items, :data_datetime, :datetime
5
- add_column :contents_core_items, :data_file, :string # , null: false, default: ''
6
- add_column :contents_core_items, :data_float, :float
7
- add_column :contents_core_items, :data_hash, :text
8
- add_column :contents_core_items, :data_integer, :integer
9
- add_column :contents_core_items, :data_string, :string # , null: false, default: ''
10
- add_column :contents_core_items, :data_text, :text
11
- end
12
- end