contents_core 0.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.
- 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: []
|