removing asset collections

This commit is contained in:
did 2011-06-17 14:32:54 -07:00
parent 0878a39858
commit 21ccbb635b
15 changed files with 341 additions and 222 deletions

View File

@ -3,12 +3,7 @@ class Asset
include Mongoid::Document
include Mongoid::Timestamps
## Extensions ##
include Extensions::Asset::Vignette
include CustomFields::ProxyClassEnabler
## fields ##
field :name, :type => String
field :content_type, :type => String
field :width, :type => Integer
field :height, :type => Integer
@ -17,10 +12,10 @@ class Asset
mount_uploader :source, AssetUploader
## associations ##
embedded_in :collection, :class_name => 'AssetCollection', :inverse_of => :assets
referenced_in :site
## validations ##
validates_presence_of :name, :source
validates_presence_of :source
## behaviours ##
@ -37,10 +32,6 @@ class Asset
File.extname(self.source_filename).gsub(/^\./, '')
end
def site_id # needed by the uploader of custom fields
self.collection.site_id
end
def to_liquid
Locomotive::Liquid::Drops::Asset.new(self)
end

View File

@ -1,81 +0,0 @@
class AssetCollection
include Locomotive::Mongoid::Document
## fields ##
field :name
field :slug
field :internal, :type => Boolean, :default => false
## associations ##
referenced_in :site
embeds_many :assets, :validate => false
## behaviours ##
custom_fields_for :assets
liquid_methods :name, :ordered_assets
## callbacks ##
before_validation :normalize_slug
before_save :store_asset_positions!
after_destroy :remove_uploaded_files
## validations ##
validates_presence_of :site, :name, :slug
validates_uniqueness_of :slug, :scope => :site_id
## named scopes ##
scope :internal, :where => { :internal => true }
scope :not_internal, :where => { :internal => false }
## methods ##
def ordered_assets
self.assets.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
def assets_order
self.ordered_assets.collect(&:id).join(',')
end
def assets_order=(order)
@assets_order = order
end
def self.find_or_create_internal(site)
site.asset_collections.internal.first || site.asset_collections.create(:name => 'system', :slug => 'system', :internal => true)
end
protected
def normalize_slug
self.slug = self.name.clone if self.slug.blank? && self.name.present?
self.slug.slugify! if self.slug.present?
end
def store_asset_positions!
return if @assets_order.nil?
ids = @assets_order.split(',').collect { |id| BSON::ObjectId(id) }
ids.each_with_index do |asset_id, index|
self.assets.find(asset_id).position = index
end
self.assets.clone.each do |asset|
if !ids.include?(asset._id)
self.assets.delete(asset)
asset.send(:delete)
end
end
end
def remove_uploaded_files # callbacks are not called on each asset so we do it manually
self.assets.each do |asset|
self.asset_custom_fields.each do |field|
asset.send(:"remove_#{field._name}!") if field.kind == 'file'
end
end
end
end

View File

@ -15,7 +15,7 @@ class Site
references_many :pages, :validate => false
references_many :snippets, :dependent => :destroy, :validate => false
references_many :theme_assets, :dependent => :destroy, :validate => false
references_many :asset_collections, :dependent => :destroy, :validate => false
references_many :assets, :dependent => :destroy, :validate => false
references_many :content_types, :dependent => :destroy, :validate => false
embeds_many :memberships

View File

@ -2,26 +2,10 @@
class AssetUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include Locomotive::CarrierWave::Uploader::Asset
version :thumb, :if => :image? do
process :resize_to_fill => [50, 50]
process :convert => 'png'
end
version :medium, :if => :image? do
process :resize_to_fill => [80, 80]
process :convert => 'png'
end
version :preview, :if => :image? do
process :resize_to_fit => [880, 1100]
process :convert => 'png'
end
def store_dir
self.build_store_dir('sites', model.collection.site_id, 'assets', model.id)
self.build_store_dir('sites', model.site_id, 'assets', model.id)
end
end

View File

@ -4,7 +4,7 @@ Locomotive::Application.configure do
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the webserver when you make code changes.
config.cache_classes = false
config.cache_classes = true
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true

View File

@ -1,8 +1,18 @@
BOARD:
x bushido version
x default template
~ editable_elements: inheritable: false (Mattias) => seems to be fixed by Dirk's last pull request (#44) => content tag
- bushido version
- default template
- remove asset_collections
x site templates
- helpers
- tinyMCE plugin
- vignette.rb
- code
- ui
- rake task
- internal collection
- assign same _id
BACKLOG:

View File

@ -76,7 +76,7 @@ module Locomotive
# Load all the dynamic classes (custom fields)
begin
ContentType.all.collect(&:fetch_content_klass)
AssetCollection.all.collect(&:fetch_asset_klass)
# AssetCollection.all.collect(&:fetch_asset_klass)
rescue ::Mongoid::Errors::InvalidDatabase => e
# let assume it's because of the first install (meaning no config.yml file)
end

View File

@ -1,87 +1,87 @@
module Locomotive
module Import
class AssetCollections < Base
def process
asset_collections = database['site']['asset_collections']
return if asset_collections.nil?
asset_collections.each do |name, attributes|
self.log "slug = #{attributes['slug']}"
asset_collection = site.asset_collections.where(:slug => attributes['slug']).first
asset_collection ||= self.build_asset_collection(attributes.merge(:name => name))
self.add_or_update_fields(asset_collection, attributes['fields'])
if options[:samples] && attributes['assets']
self.insert_samples(asset_collection, attributes['assets'])
end
asset_collection.save!
site.reload
end
end
protected
def build_asset_collection(data)
attributes = { :internal => false }.merge(data)
attributes.delete_if { |name, value| %w{fields assets}.include?(name) }
site.asset_collections.build(attributes)
end
def add_or_update_fields(asset_collection, fields)
return if fields.blank?
fields.each_with_index do |data, position|
name, data = data.keys.first, data.values.first
attributes = { :_alias => name, :label => name.humanize, :kind => 'string', :position => position }.merge(data).symbolize_keys
field = asset_collection.asset_custom_fields.detect { |f| f._alias == attributes[:_alias] }
field ||= asset_collection.asset_custom_fields.build(attributes)
field.send(:set_unique_name!) if field.new_record?
field.attributes = attributes
end
end
def insert_samples(asset_collection, assets)
assets.each_with_index do |data, position|
value, attributes = data.is_a?(Array) ? [data.first, data.last] : [data.keys.first, data.values.first]
url = attributes.delete('url')
# build with default attributes
asset = asset_collection.assets.build(:name => value, :position => position, :source => self.open_sample_asset(url))
attributes.each do |name, value|
field = asset_collection.asset_custom_fields.detect { |f| f._alias == name }
value = (case field.kind.downcase
when 'file' then self.open_sample_asset(value)
when 'boolean' then Boolean.set(value)
else
value
end)
asset.send("#{name}=", value)
end
asset.save
self.log "insert asset '#{asset.name}'"
end
end
end
end
end
# module Locomotive
# module Import
# class AssetCollections < Base
#
# def process
# asset_collections = database['site']['asset_collections']
#
# return if asset_collections.nil?
#
# asset_collections.each do |name, attributes|
# self.log "slug = #{attributes['slug']}"
#
# asset_collection = site.asset_collections.where(:slug => attributes['slug']).first
#
# asset_collection ||= self.build_asset_collection(attributes.merge(:name => name))
#
# self.add_or_update_fields(asset_collection, attributes['fields'])
#
# if options[:samples] && attributes['assets']
# self.insert_samples(asset_collection, attributes['assets'])
# end
#
# asset_collection.save!
#
# site.reload
# end
# end
#
# protected
#
# def build_asset_collection(data)
# attributes = { :internal => false }.merge(data)
#
# attributes.delete_if { |name, value| %w{fields assets}.include?(name) }
#
# site.asset_collections.build(attributes)
# end
#
# def add_or_update_fields(asset_collection, fields)
# return if fields.blank?
#
# fields.each_with_index do |data, position|
# name, data = data.keys.first, data.values.first
#
# attributes = { :_alias => name, :label => name.humanize, :kind => 'string', :position => position }.merge(data).symbolize_keys
#
# field = asset_collection.asset_custom_fields.detect { |f| f._alias == attributes[:_alias] }
#
# field ||= asset_collection.asset_custom_fields.build(attributes)
#
# field.send(:set_unique_name!) if field.new_record?
#
# field.attributes = attributes
# end
# end
#
# def insert_samples(asset_collection, assets)
# assets.each_with_index do |data, position|
# value, attributes = data.is_a?(Array) ? [data.first, data.last] : [data.keys.first, data.values.first]
#
# url = attributes.delete('url')
#
# # build with default attributes
# asset = asset_collection.assets.build(:name => value, :position => position, :source => self.open_sample_asset(url))
#
# attributes.each do |name, value|
# field = asset_collection.asset_custom_fields.detect { |f| f._alias == name }
#
# value = (case field.kind.downcase
# when 'file' then self.open_sample_asset(value)
# when 'boolean' then Boolean.set(value)
# else
# value
# end)
#
# asset.send("#{name}=", value)
# end
#
# asset.save
#
# self.log "insert asset '#{asset.name}'"
# end
# end
#
# end
# end
# end

View File

@ -1,3 +1,5 @@
require 'ostruct'
module Locomotive
module Import
class ContentTypes < Base
@ -5,6 +7,8 @@ module Locomotive
def process
return if content_types.nil?
contents_with_associations, content_types_with_associations = [], []
content_types.each do |name, attributes|
self.log "[content_types] slug = #{attributes['slug']}"
@ -14,6 +18,10 @@ module Locomotive
self.add_or_update_fields(content_type, attributes['fields'])
if content_type.content_custom_fields.any? { |f| ['has_many', 'has_one'].include?(f.kind) }
content_types_with_associations << content_type
end
self.set_highlighted_field_name(content_type)
self.set_order_by_value(content_type)
@ -21,17 +29,21 @@ module Locomotive
self.set_group_by_value(content_type)
if options[:samples] && attributes['contents']
self.insert_samples(content_type, attributes['contents'])
contents_with_associations += self.insert_samples(content_type, attributes['contents'])
end
content_type.save!
site.reload
end
# look up for associations and replace their target field by the real class name
self.replace_target(content_types_with_associations)
# update all the contents with associations now that every content is stored in mongodb
self.insert_samples_with_associations(contents_with_associations)
# invalidate the cache of the dynamic classes (custom fields)
ContentType.all.collect(&:invalidate_content_klass)
AssetCollection.all.collect(&:invalidate_asset_klass)
site.content_types.all.collect { |c| c.invalidate_content_klass; c.fetch_content_klass }
site.asset_collections.all.collect { |c| c.invalidate_content_klass; c.fetch_content_klass }
end
protected
@ -61,13 +73,33 @@ module Locomotive
field.send(:set_unique_name!) if field.new_record?
field.attributes = attributes
field[:kind] = field[:kind].downcase # old versions of the kind are capitalized
end
end
def replace_target(content_types)
content_types.each do |content_type|
content_type.content_custom_fields.each do |field|
next unless ['has_many', 'has_one'].include?(field.kind)
target_content_type = site.content_types.where(:name => field.target).first
field.target = target_content_type.content_klass.to_s if target_content_type
end
content_type.save
end
end
def insert_samples(content_type, contents)
contents_with_associations = []
contents.each_with_index do |data, position|
value, attributes = data.is_a?(Array) ? [data.first, data.last] : [data.keys.first, data.values.first]
associations = []
# build with default attributes
content = content_type.contents.build(content_type.highlighted_field_name.to_sym => value, :_position_in_list => position)
@ -76,7 +108,14 @@ module Locomotive
next if field.nil? # the attribute name is not related to a field (name misspelled ?)
value = (case field.kind.downcase
kind = field.kind
if ['has_many', 'has_one'].include?(kind)
associations << OpenStruct.new(:name => name, :kind => kind, :value => value, :target => field.target)
next
end
value = (case kind
when 'file' then self.open_sample_asset(value)
when 'boolean' then Boolean.set(value)
when 'date' then value.is_a?(Date) ? value : Date.parse(value)
@ -85,17 +124,50 @@ module Locomotive
field.category_items.build :name => value
end
value
else
else # string, text
value
end)
content.send("#{name}=", value)
end
content.save
content.save(:validate => false)
contents_with_associations << [content, associations] unless associations.empty?
self.log "insert content '#{content.send(content_type.highlighted_field_name.to_sym)}'"
end
contents_with_associations
end
def insert_samples_with_associations(contents)
contents.each do |content_information|
next if content_information.empty?
content, associations = content_information
content = content._parent.reload.contents.find(content._id) # target should be updated
associations.each do |association|
target_content_type = site.content_types.where(:name => association.target).first
next if target_content_type.nil?
value = (case association.kind
when 'has_one' then
target_content_type.contents.detect { |c| c.highlighted_field_value == association.value }
when 'has_many' then
association.value.collect do |v|
target_content_type.contents.detect { |c| c.highlighted_field_value == v }._id
end
end)
content.send("#{association.name}=", value)
end
content.save
end
end
def set_highlighted_field_name(content_type)

View File

@ -30,7 +30,7 @@ module Locomotive
self.unzip!
raise "No database.yml found in the theme zipfile" if @database.nil?
raise "No config/compiled_site.yml found in the theme zipfile" if @database.nil?
context = {
:database => @database,
@ -42,7 +42,7 @@ module Locomotive
self.reset! if @options[:reset]
%w(site content_types assets asset_collections snippets pages).each do |step|
%w(site content_types assets snippets pages).each do |step|
if @options[:enabled][step] != false
"Locomotive::Import::#{step.camelize}".constantize.process(context, @options)
@worker.update_attributes :step => step if @worker
@ -145,10 +145,10 @@ module Locomotive
zipfile.each do |entry|
next if entry.name =~ /__MACOSX/
if entry.name =~ /database.yml$/
if entry.name =~ /config\/compiled_site.yml$/
@database = YAML.load(zipfile.read(entry.name))
@theme_path = File.join(destination_path, entry.name.gsub('database.yml', ''))
@theme_path = File.join(destination_path, entry.name.gsub('config/compiled_site.yml', ''))
next
end

View File

@ -9,9 +9,9 @@ module Locomotive
self.add_page('index')
Dir[File.join(theme_path, 'templates', '**/*')].each do |template_path|
Dir[File.join(theme_path, 'app', 'views', 'pages', '**/*')].each do |template_path|
fullpath = template_path.gsub(File.join(theme_path, 'templates'), '').gsub('.liquid', '').gsub(/^\//, '')
fullpath = template_path.gsub(File.join(theme_path, 'app', 'views', 'pages'), '').gsub('.liquid', '').gsub(/^\//, '')
next if %w(index 404).include?(fullpath)
@ -37,7 +37,7 @@ module Locomotive
return page if page # already added, so skip it
template = File.read(File.join(theme_path, 'templates', "#{fullpath}.liquid")) rescue "Unable to find #{fullpath}.liquid"
template = File.read(File.join(theme_path, 'app', 'views', 'pages', "#{fullpath}.liquid")) rescue "Unable to find #{fullpath}.liquid"
self.replace_images!(template)
@ -146,8 +146,6 @@ module Locomotive
fullpath = data.keys.first.to_s
unless %w(index 404).include?(fullpath)
# position = fullpath == 'index' ? 0 : 1
# else
(segments = fullpath.split('/')).pop
position_key = segments.empty? ? 'index' : segments.join('/')

View File

@ -3,14 +3,14 @@ module Locomotive
class Snippets < Base
def process
Dir[File.join(theme_path, 'snippets', '*')].each do |snippet_path|
Dir[File.join(theme_path, 'app', 'views', 'snippets', '*')].each do |snippet_path|
self.log "path = #{snippet_path}"
name = File.basename(snippet_path, File.extname(snippet_path)).parameterize('_')
snippet = site.snippets.where(:slug => name).first || site.snippets.build(:name => name)
snippet.template = File.read(snippet_path) # = site.snippets.create! :name => name, :template =>
snippet.template = File.read(snippet_path)
snippet.save!
end

View File

@ -1,3 +1,7 @@
require 'locomotive'
# encoding: utf-8
namespace :locomotive do
desc 'Fetch the Locomotive default site template for the installation'
@ -7,6 +11,145 @@ namespace :locomotive do
puts '...done'
end
namespace :upgrade do
desc 'Remove asset collections and convert them into content types'
task :remove_asset_collections => :environment do
puts "asset_collection # ? #{AssetCollection.count}"
AssetCollection.all.each do |collection|
site = Site.find(collection.attributes['site_id'])
if collection.internal?
# internal collection => create simple assets without associated to a collection
collection.assets.each do |tmp_asset|
sanitized_attributes = tmp_asset.attributes.dup
sanitized_attributes.delete_if { |k, v| [:name, :source_filename].include?(k) }
sanitized_attributes[:_id] = tmp_asset._id
asset = site.assets.build(sanitized_attributes)
asset.source = tmp_asset.source.file
asset.save!
puts "asset = #{asset.inspect}"
asset.destroy
end
else
# create content_types reflection of an asset collection
content_type = site.content_types.build({
:name => collection.name,
:slug => collection.slug,
:order_by => 'manually'
})
# add default custom fields
content_type.content_custom_fields.build(:label => 'Name', :slug => 'name', :kind => 'string', :required => true)
content_type.content_custom_fields.build(:label => 'Source', :slug => 'source', :kind => 'file', :required => true)
# extra custom fields
collection.asset_custom_fields.each do |field|
content_type.content_custom_fields.build(field.attributes)
end
# puts "new content_type = #{content_type.inspect}"
# puts "valid ? #{content_type.valid?.inspect}"
content_type.save
content_type = ContentType.find(content_type._id)
# insert data
# puts "collection.assets = #{collection.ordered_assets.inspect} / #{collection.ordered_assets.class}"
collection.ordered_assets.each do |asset|
attributes = (if asset.custom_fields.blank?
{ :created_at => asset.created_at, :updated_at => asset.updated_at }
else
asset.aliased_attributes
end)
attributes.merge!(:name => asset.name, :_position_in_list => asset.position)
# puts "attributes = #{attributes.inspect}"
content = content_type.contents.build(attributes)
content.source = asset.source.file
content.save!
puts "content = #{content.inspect}"
end
content_type.destroy
end
end
end
end
end
class TmpAssetUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include Locomotive::CarrierWave::Uploader::Asset
version :thumb, :if => :image? do
process :resize_to_fill => [50, 50]
process :convert => 'png'
end
version :medium, :if => :image? do
process :resize_to_fill => [80, 80]
process :convert => 'png'
end
version :preview, :if => :image? do
process :resize_to_fit => [880, 1100]
process :convert => 'png'
end
def store_dir
self.build_store_dir('sites', model.collection.site_id, 'assets', model.id)
end
end
# Classes only used during the upgrade mechanism. Will be removed in a few weeks
class AssetCollection
include Locomotive::Mongoid::Document
field :name
field :slug
field :internal, :type => Boolean, :default => false
referenced_in :site
embeds_many :assets, :class_name => 'TmpAsset', :validate => false
custom_fields_for :assets
after_destroy :remove_uploaded_files
scope :internal, :where => { :internal => true }
scope :not_internal, :where => { :internal => false }
def ordered_assets
self.assets.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
def self.find_or_create_internal(site)
site.asset_collections.internal.first || site.asset_collections.create(:name => 'system', :slug => 'system', :internal => true)
end
end
class TmpAsset
include Mongoid::Document
include Mongoid::Timestamps
include CustomFields::ProxyClassEnabler
field :name, :type => String
field :content_type, :type => String
field :width, :type => Integer
field :height, :type => Integer
field :size, :type => Integer
field :position, :type => Integer, :default => 0
mount_uploader :source, TmpAssetUploader
embedded_in :collection, :class_name => 'AssetCollection', :inverse_of => :assets
def site_id
self.collection.site_id
end
end

Binary file not shown.

View File

@ -20,9 +20,9 @@ describe Locomotive::Import::Job do
end
it 'adds content types' do
@site.content_types.count.should == 2
@site.content_types.count.should == 4
content_type = @site.content_types.where(:slug => 'projects').first
content_type.content_custom_fields.size.should == 7
content_type.content_custom_fields.size.should == 9
end
it 'converts correctly the order_by option for content types' do
@ -38,6 +38,8 @@ describe Locomotive::Import::Job do
content.name.should == 'Locomotive App'
content.thumbnail.url.should_not be_nil
content.featured.should == true
content.client.name.should == 'My client #1'
content.team.first.name.should == 'Michael Scott'
end
it 'inserts theme assets' do