contents_core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +101 -0
- data/Rakefile +36 -0
- data/app/models/contents_core/application_record.rb +5 -0
- data/app/models/contents_core/block.rb +170 -0
- data/app/models/contents_core/item.rb +50 -0
- data/app/models/contents_core/item_boolean.rb +23 -0
- data/app/models/contents_core/item_datetime.rb +14 -0
- data/app/models/contents_core/item_file.rb +46 -0
- data/app/models/contents_core/item_float.rb +23 -0
- data/app/models/contents_core/item_hash.rb +16 -0
- data/app/models/contents_core/item_integer.rb +23 -0
- data/app/models/contents_core/item_string.rb +29 -0
- data/app/models/contents_core/item_text.rb +19 -0
- data/config/initializers/contents_core.rb +73 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20170414173603_create_contents_core_blocks.rb +17 -0
- data/db/migrate/20170414173610_create_contents_core_items.rb +11 -0
- data/db/migrate/20170414173611_add_extra_items.rb +12 -0
- data/lib/contents_core.rb +14 -0
- data/lib/contents_core/blocks.rb +34 -0
- data/lib/contents_core/engine.rb +7 -0
- data/lib/contents_core/version.rb +3 -0
- data/lib/tasks/contents_core_tasks.rake +4 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3e33ec3a230bad84970e4f2b43fe3c700e9ff72a
|
4
|
+
data.tar.gz: 9d3f7d53a033c6d52b5d22f580ec3210c90c17a0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 566cb403e239475914c934bd85dd1fb37a1a37ad50ede3ca7701f24c61ebcabb96de6e4ee171ebb90dc53700c0bf745fa1ed71b32bd2cef70ce6172399d7cf95
|
7
|
+
data.tar.gz: f07a30421f8d0de813666cc48d0d5b00902d8f07f87553abdf85f17c30a306fec0bda45bcec911ecb9776561181825dd446b440037c27b38b7bb7adf253a6263
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# ContentsCore
|
2
|
+
|
3
|
+
A Rails gem which offer a simple structure to manage contents in a flexible way.
|
4
|
+
|
5
|
+
_NOTE_: this is an **ALPHA** version, major changes could happens - this is a refactoring of another gem of mine (editable_components)
|
6
|
+
|
7
|
+
Goals:
|
8
|
+
|
9
|
+
- attach the necessary data to a model transparently
|
10
|
+
|
11
|
+
- simplify the components development in views
|
12
|
+
|
13
|
+
- add blocks / items (= fields) without migrations
|
14
|
+
|
15
|
+
### Install
|
16
|
+
|
17
|
+
- Add to the Gemfile:
|
18
|
+
`gem 'contents_core'`
|
19
|
+
|
20
|
+
- Copy migrations (Rails 5.x syntax, in Rails 4.x use `rake`):
|
21
|
+
`rails contents_core:install:migrations`
|
22
|
+
|
23
|
+
- Migrate
|
24
|
+
|
25
|
+
- Add the concern *Blocks* to your model: `include ContentsCore::Blocks`
|
26
|
+
|
27
|
+
### Config
|
28
|
+
|
29
|
+
Edit the conf file: `config/initializers/contents_core.rb`
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
conf = ContentsCore.config
|
33
|
+
# Adds a new custom block
|
34
|
+
conf[:cc_blocks][:custom] = {
|
35
|
+
name: 'Custom block',
|
36
|
+
items: {
|
37
|
+
int1: :item_integer,
|
38
|
+
int2: :item_integer,
|
39
|
+
a_float: :item_float
|
40
|
+
}
|
41
|
+
}
|
42
|
+
ContentsCore.config( { components: conf[:cc_blocks] } )
|
43
|
+
```
|
44
|
+
|
45
|
+
Create the new view blocks: `app/views/contents_core/_block_custom.html.erb`
|
46
|
+
|
47
|
+
```erb
|
48
|
+
<% if local_assigns[:block] %>
|
49
|
+
<% block = local_assigns[:block] %>
|
50
|
+
<div <%= block.editable %>>
|
51
|
+
1st number: <span class="num1"<%= block.props.integers[0].editable %>><%= block.props.integers[0] %></span>
|
52
|
+
- 2nd number: <span class="num2"<%= block.props.integers[1].editable %>><%= block.props.integers[1] %></span><br/>
|
53
|
+
A float: <span <%= block.props.float.editable %>><%= block.props.float %></span><br/>
|
54
|
+
</div>
|
55
|
+
<% end %>
|
56
|
+
```
|
57
|
+
|
58
|
+
##### Images
|
59
|
+
|
60
|
+
To add support for images add CarrierWave gem to your Gemfile and execute: `rails generate uploader File` and mount it in the model *app/models/contents_core/item_file.rb*:
|
61
|
+
|
62
|
+
```rb
|
63
|
+
module ContentsCore
|
64
|
+
class ItemFile < Item
|
65
|
+
mount_uploader :data_file, ImageUploader
|
66
|
+
|
67
|
+
alias_attribute :data, :data_file
|
68
|
+
|
69
|
+
def editable
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
def init
|
74
|
+
self.data_file = File.open( Rails.root.join( 'public', 'images', 'original', 'missing.jpg' ) )
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.type_name
|
79
|
+
'file'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
##### Custom blocks
|
86
|
+
|
87
|
+
To create a "free form" block just use: `Page.first.create_block :intro, name: 'IntroBlock', schema: { intro: :item_string, subtitle: :item_string }`
|
88
|
+
|
89
|
+
### Dev Notes
|
90
|
+
|
91
|
+
##### Structure
|
92
|
+
|
93
|
+
- Including the Editable concern to a model will add `has_many :ec_blocks` relationship (the list of blocks attached to a container) and some utility methods
|
94
|
+
|
95
|
+
- Block: an editable UI component (ex. a text with a title, a slider, a 3 column text widgets, etc.); built with a list of sub blocks (for nested components) and a list of items
|
96
|
+
|
97
|
+
- Item: a single piece of information (ex. a string, a text, a boolean, an integer, a file, etc.)
|
98
|
+
|
99
|
+
## Contributors
|
100
|
+
|
101
|
+
- [Mattia Roccoberton](http://blocknot.es) - creator, maintainer
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'ContentsCore'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
require 'bundler/gem_tasks'
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'test'
|
31
|
+
t.pattern = 'test/**/*_test.rb'
|
32
|
+
t.verbose = false
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
task default: :test
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class Block < ApplicationRecord
|
3
|
+
EMPTY_DATA = OpenStruct.new( { data: '' } )
|
4
|
+
|
5
|
+
# --- fields options ----------------------------------------------------- #
|
6
|
+
serialize :options, JSON
|
7
|
+
serialize :validations, JSON
|
8
|
+
|
9
|
+
# --- relations ---------------------------------------------------------- #
|
10
|
+
belongs_to :parent, polymorphic: true
|
11
|
+
has_many :cc_blocks, as: :parent, dependent: :destroy, foreign_key: 'parent_id', class_name: 'Block'
|
12
|
+
has_many :items, dependent: :destroy
|
13
|
+
accepts_nested_attributes_for :cc_blocks, allow_destroy: true
|
14
|
+
accepts_nested_attributes_for :items
|
15
|
+
|
16
|
+
# --- hooks -------------------------------------------------------------- #
|
17
|
+
before_create :on_before_create
|
18
|
+
after_create :on_after_create
|
19
|
+
|
20
|
+
# --- scopes ------------------------------------------------------------- #
|
21
|
+
default_scope { order( :position ) }
|
22
|
+
scope :published, -> { where( published: true ) unless ContentsCore.editing }
|
23
|
+
scope :with_nested, -> { includes( :items, cc_blocks: :items ) }
|
24
|
+
|
25
|
+
# --- validations -------------------------------------------------------- #
|
26
|
+
validates_presence_of :block_type, :position
|
27
|
+
|
28
|
+
# --- misc --------------------------------------------------------------- #
|
29
|
+
## amoeba do
|
30
|
+
## enable
|
31
|
+
## # customize( lambda { |original_obj, new_obj|
|
32
|
+
## # original_obj.unit_pictures.each{|p| new_obj.unit_pictures.new(:data => File.open(p.data.file.path))}
|
33
|
+
## # })
|
34
|
+
## end
|
35
|
+
|
36
|
+
# after_validation :on_after_validation
|
37
|
+
#
|
38
|
+
# field :block_type, type: String, default: 'text'
|
39
|
+
# field :name, type: String, default: ''
|
40
|
+
# field :position, type: Integer, default: 0
|
41
|
+
# field :published, type: Mongoid::Boolean, default: true
|
42
|
+
# field :_init, type: Mongoid::Boolean, default: false
|
43
|
+
#
|
44
|
+
# embedded_in :parent, polymorphic: true
|
45
|
+
#
|
46
|
+
# embeds_many :cc_blocks, cascade_callbacks: true, order: :position.desc, class_name: 'ContentsCore::Block'
|
47
|
+
# embeds_many :items, cascade_callbacks: true, class_name: 'ContentsCore::Item'
|
48
|
+
#
|
49
|
+
# accepts_nested_attributes_for :cc_blocks, allow_destroy: true
|
50
|
+
# accepts_nested_attributes_for :items
|
51
|
+
#
|
52
|
+
# # scope :published, -> { where( published: true ) unless ApplicationController.edit_mode }
|
53
|
+
|
54
|
+
def attr_id
|
55
|
+
"#{self.class.to_s.split('::').last}-#{self.id}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def children_type
|
59
|
+
ContentsCore.config[:cc_blocks][block_type.to_sym][:children_type]
|
60
|
+
end
|
61
|
+
|
62
|
+
def editable
|
63
|
+
ContentsCore.editing ? (
|
64
|
+
is_sub_block? ?
|
65
|
+
{
|
66
|
+
'data-ec-sub-block': self.id,
|
67
|
+
'data-ec-ct': self.block_type,
|
68
|
+
'data-ec-position': self.position,
|
69
|
+
'data-ec-pub': self.published
|
70
|
+
} :
|
71
|
+
{
|
72
|
+
'data-ec-block': self.id,
|
73
|
+
'data-ec-container': self.children_type,
|
74
|
+
'data-ec-ct': self.block_type,
|
75
|
+
'data-ec-pub': self.published
|
76
|
+
}
|
77
|
+
).map { |k, v| "#{k}=\"#{v}\"" }.join( ' ' ).html_safe : ''
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns an item by name
|
81
|
+
def get( name )
|
82
|
+
unless @_items
|
83
|
+
@_items = {}
|
84
|
+
items.each { |item| @_items[item.name] = item }
|
85
|
+
end
|
86
|
+
@_items[name]
|
87
|
+
end
|
88
|
+
|
89
|
+
def has_parent?
|
90
|
+
parent.present?
|
91
|
+
end
|
92
|
+
|
93
|
+
def has_children?
|
94
|
+
cc_blocks.exists?
|
95
|
+
end
|
96
|
+
|
97
|
+
def is_sub_block?
|
98
|
+
parent.present? && parent_type == 'ContentsCore::Block'
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_after_create
|
102
|
+
# TODO: validates type before creation!
|
103
|
+
t = self.block_type.to_sym
|
104
|
+
Block::init_items( self, ContentsCore.config[:cc_blocks][t][:items] ) if Block::block_types.include?( t )
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_before_create
|
108
|
+
if self.name.blank?
|
109
|
+
names = parent.cc_blocks.map &:name
|
110
|
+
i = 0
|
111
|
+
while( ( i += 1 ) < 1000 ) # Search an empty group
|
112
|
+
unless names.include? "#{block_type}-#{i}"
|
113
|
+
self.name = "#{block_type}-#{i}"
|
114
|
+
break
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def props
|
121
|
+
pieces = {}
|
122
|
+
|
123
|
+
Item::item_types.each do |type|
|
124
|
+
pieces[type.pluralize.to_sym] = []
|
125
|
+
end
|
126
|
+
items.each do |item| # TODO: improve me
|
127
|
+
pieces[item.class.type_name.pluralize.to_sym].push item
|
128
|
+
end
|
129
|
+
Item::item_types.each do |type|
|
130
|
+
pieces[type.to_sym] = pieces[type.pluralize.to_sym].any? ? pieces[type.pluralize.to_sym].first : nil # EMPTY_DATA - empty Item per sti class?
|
131
|
+
end
|
132
|
+
|
133
|
+
# pieces = {
|
134
|
+
# images: items.select { |item| item.type == ItemImage.to_s },
|
135
|
+
# integers: items.select { |item| item.type == ItemInteger.to_s },
|
136
|
+
# strings: items.select { |item| item.type == ItemString.to_s },
|
137
|
+
# texts: items.select { |item| item.type == ItemText.to_s },
|
138
|
+
# }
|
139
|
+
|
140
|
+
# pieces[:image] = pieces[:images].any? ? pieces[:images].first : EMPTY_DATA
|
141
|
+
# pieces[:integers] = pieces[:integers].any? ? pieces[:integers].first : EMPTY_DATA
|
142
|
+
# pieces[:string] = pieces[:strings].any? ? pieces[:strings].first : EMPTY_DATA
|
143
|
+
# pieces[:text] = pieces[:texts].any? ? pieces[:texts].first : EMPTY_DATA
|
144
|
+
|
145
|
+
OpenStruct.new( pieces )
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.block_types
|
149
|
+
@@block_types ||= ContentsCore.config[:cc_blocks].keys
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.init_items( block, items )
|
153
|
+
items.each do |name, type|
|
154
|
+
t = type.to_sym
|
155
|
+
if type.to_s.start_with? 'item_'
|
156
|
+
c = 'ContentsCore::' + ActiveSupport::Inflector.camelize( t )
|
157
|
+
begin
|
158
|
+
model = c.constantize
|
159
|
+
rescue Exception => e
|
160
|
+
Rails.logger.error '[ERROR] ContentsCore - init_items: ' + e.message
|
161
|
+
model = false
|
162
|
+
end
|
163
|
+
block.items << model.new( name: name ).init if model
|
164
|
+
elsif Block::block_types.include? t.to_sym
|
165
|
+
block.cc_blocks << ( cmp = Block.new( block_type: t, name: name ) )
|
166
|
+
end
|
167
|
+
end if items
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class Item < ApplicationRecord
|
3
|
+
# field :data, type: String
|
4
|
+
|
5
|
+
# embedded_in :cc_blocks
|
6
|
+
|
7
|
+
belongs_to :block
|
8
|
+
|
9
|
+
def attr_id
|
10
|
+
"#{self.class_name}-#{self.id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def class_name
|
14
|
+
self.class.to_s.split('::').last
|
15
|
+
end
|
16
|
+
|
17
|
+
def editable
|
18
|
+
ContentsCore.editing ? " data-ec-item=\"#{self.id}\" data-ec-input=\"#{self.opt_input}\" data-ec-type=\"#{self.class_name}\"".html_safe : ''
|
19
|
+
end
|
20
|
+
|
21
|
+
def opt_input
|
22
|
+
if self.block.options[self.name] && self.block.options[self.name]['input']
|
23
|
+
self.block.options[self.name]['input'].to_s
|
24
|
+
elsif config[:input]
|
25
|
+
config[:input].to_s
|
26
|
+
else
|
27
|
+
''
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
self.data
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_data( value )
|
36
|
+
self.data = value
|
37
|
+
self.save
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.item_types
|
41
|
+
@@item_types ||= ContentsCore.config[:items].keys.map &:to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def config
|
47
|
+
@config ||= ContentsCore.config[:items][self.class::type_name.to_sym] ? ContentsCore.config[:items][self.class::type_name.to_sym] : {}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class ItemBoolean < Item
|
3
|
+
alias_attribute :data, :data_boolean
|
4
|
+
|
5
|
+
def init
|
6
|
+
self.data = 0
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_data( value )
|
11
|
+
self.data = ( value == 'true' ) ? 1 : 0
|
12
|
+
self.save
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
self.data > 0 ? 'true' : 'false'
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.type_name
|
20
|
+
'boolean'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class ItemFile < Item
|
3
|
+
# has_attached_file :data_string
|
4
|
+
# validates_attachment_content_type :data_string, content_type: ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
|
5
|
+
|
6
|
+
# include Mongoid::Paperclip
|
7
|
+
# has_mongoid_attached_file :data
|
8
|
+
# validates_attachment_content_type :data, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
|
9
|
+
|
10
|
+
# mount_uploader :data_file, ::FileUploader
|
11
|
+
|
12
|
+
alias_attribute :data, :data_file
|
13
|
+
|
14
|
+
def editable
|
15
|
+
ContentsCore.editing ? { 'data-ec-item': self.id, 'data-ec-input': self.opt_input, 'data-ec-type': self.class_name, 'data-ec-block': self.block_id } : {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def init
|
19
|
+
# self.data_file = File.open( ContentsCore::Engine.root.join( 'lib', 'data', 'img1.jpg' ) )
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.type_name
|
24
|
+
'file'
|
25
|
+
end
|
26
|
+
|
27
|
+
# before_validation :on_before_validation
|
28
|
+
#
|
29
|
+
# def on_before_validation
|
30
|
+
# self.data = File.open( 'rails/editable_components_test/public/img/img1.jpg' )
|
31
|
+
# # binding.pry
|
32
|
+
# end
|
33
|
+
|
34
|
+
# before_save :on_before_save
|
35
|
+
# before_validation :on_before_validation
|
36
|
+
# before_create :on_before_create
|
37
|
+
# before_update :on_before_update
|
38
|
+
# before_upsert :on_before_upsert
|
39
|
+
#
|
40
|
+
# def on_before_save; p '>>> before_save'; end
|
41
|
+
# def on_before_validation; p '>>> before_validation'; end
|
42
|
+
# def on_before_create; p '>>> before_create'; end
|
43
|
+
# def on_before_update; p '>>> before_update'; end
|
44
|
+
# def on_before_upsert; p '>>> before_upsert'; end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class ItemFloat < Item
|
3
|
+
alias_attribute :data, :data_float
|
4
|
+
|
5
|
+
def init
|
6
|
+
self.data = 0
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_data( value )
|
11
|
+
self.data = value.to_f
|
12
|
+
self.save
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
self.data.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.type_name
|
20
|
+
'float'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class ItemInteger < Item
|
3
|
+
alias_attribute :data, :data_integer
|
4
|
+
|
5
|
+
def init
|
6
|
+
self.data = 0
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_data( value )
|
11
|
+
self.data = value.to_i
|
12
|
+
self.save
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
self.data.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.type_name
|
20
|
+
'integer'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class ItemString < Item
|
3
|
+
alias_attribute :data, :data_string
|
4
|
+
|
5
|
+
validate :on_validate
|
6
|
+
|
7
|
+
def init
|
8
|
+
self.data = self.name.gsub( /-/, ' ' ).humanize
|
9
|
+
self
|
10
|
+
end
|
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
|
19
|
+
|
20
|
+
def update_data( value )
|
21
|
+
self.data = ActionController::Base.helpers.sanitize( value, tags: [] )
|
22
|
+
self.save
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.type_name
|
26
|
+
'string'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
class ItemText < Item
|
3
|
+
alias_attribute :data, :data_text
|
4
|
+
|
5
|
+
def init
|
6
|
+
self.data = self.name.gsub( /-/, ' ' ).humanize
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_data( value )
|
11
|
+
self.data = ActionController::Base.helpers.sanitize( CGI.unescapeHTML( value ) )
|
12
|
+
self.save
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.type_name
|
16
|
+
'text'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
@@editing = false
|
3
|
+
|
4
|
+
@@config = {
|
5
|
+
cc_blocks: {
|
6
|
+
multi_text: {
|
7
|
+
children_type: :text,
|
8
|
+
name: 'Multi columns block',
|
9
|
+
items: {
|
10
|
+
column: :text
|
11
|
+
}
|
12
|
+
},
|
13
|
+
text: {
|
14
|
+
name: 'Text block',
|
15
|
+
items: {
|
16
|
+
title: :item_string,
|
17
|
+
content: :item_text
|
18
|
+
}
|
19
|
+
},
|
20
|
+
},
|
21
|
+
items: {
|
22
|
+
boolean: {},
|
23
|
+
datetime: {},
|
24
|
+
float: {},
|
25
|
+
hash: {},
|
26
|
+
file: {
|
27
|
+
input: :file_image
|
28
|
+
},
|
29
|
+
integer: {},
|
30
|
+
string: {},
|
31
|
+
text: {
|
32
|
+
input: :html
|
33
|
+
},
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
if defined? CarrierWave
|
38
|
+
@@config[:cc_blocks].merge!({
|
39
|
+
image: {
|
40
|
+
name: 'Image block',
|
41
|
+
items: {
|
42
|
+
img: :item_file
|
43
|
+
}
|
44
|
+
},
|
45
|
+
slide: {
|
46
|
+
name: 'Slide block',
|
47
|
+
items: {
|
48
|
+
img: :item_file,
|
49
|
+
title: :item_string
|
50
|
+
}
|
51
|
+
},
|
52
|
+
slider: {
|
53
|
+
children_type: :slide,
|
54
|
+
name: 'Slider block',
|
55
|
+
items: {
|
56
|
+
slide: :slide
|
57
|
+
}
|
58
|
+
},
|
59
|
+
text_with_image: {
|
60
|
+
name: 'Text with image block',
|
61
|
+
items: {
|
62
|
+
img: :item_file,
|
63
|
+
title: :item_string,
|
64
|
+
content: :item_text
|
65
|
+
}
|
66
|
+
},
|
67
|
+
})
|
68
|
+
|
69
|
+
# ItemImage.class_eval do
|
70
|
+
# mount_uploader :data, ImageUploader
|
71
|
+
# end
|
72
|
+
end
|
73
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateContentsCoreBlocks < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
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.integer :position, null: false, default: 0
|
8
|
+
t.boolean :published, null: false, default: true
|
9
|
+
t.string :options, null: false, default: '{}'
|
10
|
+
t.string :validations, null: false, default: '{}'
|
11
|
+
t.integer :parent_id
|
12
|
+
t.string :parent_type
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index :contents_core_blocks, [:parent_id, :parent_type]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateContentsCoreItems < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
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
|
7
|
+
end
|
8
|
+
|
9
|
+
add_index :contents_core_items, :block_id
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
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, :string # , null: false, default: ''
|
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
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'contents_core/blocks'
|
2
|
+
require 'contents_core/engine'
|
3
|
+
|
4
|
+
module ContentsCore
|
5
|
+
def self.config( options = {} )
|
6
|
+
@@config.merge! options
|
7
|
+
@@config
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.editing( editing = nil )
|
11
|
+
@@editing = editing unless editing.nil?
|
12
|
+
@@editing
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ContentsCore
|
2
|
+
module Blocks
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
# embeds_many :cc_blocks, as: :parent, cascade_callbacks: true, order: :position.desc, class_name: 'ContentsCore::Block'
|
7
|
+
has_many :cc_blocks, as: :parent, dependent: :destroy, foreign_key: 'parent_id', class_name: Block.to_s
|
8
|
+
accepts_nested_attributes_for :cc_blocks, allow_destroy: true
|
9
|
+
|
10
|
+
def create_block( type = :text, params = {} )
|
11
|
+
block = Block.new( block_type: type )
|
12
|
+
block.name = params[:name] if params[:name]
|
13
|
+
block.options = params[:options] if params[:options]
|
14
|
+
block.validations = params[:validations] if params[:validations]
|
15
|
+
cc_blocks << block
|
16
|
+
Block::init_items block, params[:schema] if params[:schema]
|
17
|
+
block
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_blocks( version = 0 )
|
21
|
+
return @current_blocks if @current_blocks
|
22
|
+
version = 0 unless ContentsCore.editing # no admin = only current version
|
23
|
+
@current_blocks = cc_blocks.where( version: version.to_i ).with_nested.published
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_block( name, version = 0 )
|
27
|
+
current_blocks( version ).each do |block|
|
28
|
+
return block if block.name == name
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: contents_core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mat
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5'
|
27
|
+
description: A Rails gem which offer a simple structure to manage contents in a flexible
|
28
|
+
way
|
29
|
+
email:
|
30
|
+
- mat@blocknot.es
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- MIT-LICENSE
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- app/models/contents_core/application_record.rb
|
39
|
+
- app/models/contents_core/block.rb
|
40
|
+
- app/models/contents_core/item.rb
|
41
|
+
- app/models/contents_core/item_boolean.rb
|
42
|
+
- app/models/contents_core/item_datetime.rb
|
43
|
+
- app/models/contents_core/item_file.rb
|
44
|
+
- app/models/contents_core/item_float.rb
|
45
|
+
- app/models/contents_core/item_hash.rb
|
46
|
+
- app/models/contents_core/item_integer.rb
|
47
|
+
- app/models/contents_core/item_string.rb
|
48
|
+
- app/models/contents_core/item_text.rb
|
49
|
+
- config/initializers/contents_core.rb
|
50
|
+
- config/routes.rb
|
51
|
+
- db/migrate/20170414173603_create_contents_core_blocks.rb
|
52
|
+
- db/migrate/20170414173610_create_contents_core_items.rb
|
53
|
+
- db/migrate/20170414173611_add_extra_items.rb
|
54
|
+
- lib/contents_core.rb
|
55
|
+
- lib/contents_core/blocks.rb
|
56
|
+
- lib/contents_core/engine.rb
|
57
|
+
- lib/contents_core/version.rb
|
58
|
+
- lib/tasks/contents_core_tasks.rake
|
59
|
+
homepage: https://github.com/blocknotes/contents_core
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.5.2
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: Flexible contents structure for Rails
|
83
|
+
test_files: []
|