From 21ccbb635b703e0a376df03966317bca106879db Mon Sep 17 00:00:00 2001 From: did Date: Fri, 17 Jun 2011 14:32:54 -0700 Subject: [PATCH] removing asset collections --- app/models/asset.rb | 13 +- app/models/asset_collection.rb | 81 ---------- app/models/site.rb | 2 +- app/uploaders/asset_uploader.rb | 18 +-- config/environments/development.rb | 2 +- doc/TODO | 14 +- lib/locomotive.rb | 2 +- lib/locomotive/import/asset_collections.rb | 174 ++++++++++----------- lib/locomotive/import/content_types.rb | 88 ++++++++++- lib/locomotive/import/job.rb | 8 +- lib/locomotive/import/pages.rb | 8 +- lib/locomotive/import/snippets.rb | 4 +- lib/tasks/locomotive.rake | 143 +++++++++++++++++ spec/fixtures/themes/default.zip | Bin 409953 -> 397631 bytes spec/lib/locomotive/import_spec.rb | 6 +- 15 files changed, 341 insertions(+), 222 deletions(-) delete mode 100644 app/models/asset_collection.rb diff --git a/app/models/asset.rb b/app/models/asset.rb index d2fcc646..ff1fe405 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -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 diff --git a/app/models/asset_collection.rb b/app/models/asset_collection.rb deleted file mode 100644 index da004d60..00000000 --- a/app/models/asset_collection.rb +++ /dev/null @@ -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 diff --git a/app/models/site.rb b/app/models/site.rb index 7873c6d5..724734c9 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -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 diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index c515da41..041cca08 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -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 diff --git a/config/environments/development.rb b/config/environments/development.rb index 02694487..bf677a27 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -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 diff --git a/doc/TODO b/doc/TODO index 0c45ca12..e2a5b980 100644 --- a/doc/TODO +++ b/doc/TODO @@ -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: diff --git a/lib/locomotive.rb b/lib/locomotive.rb index 6d49cb32..be35ce3d 100644 --- a/lib/locomotive.rb +++ b/lib/locomotive.rb @@ -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 diff --git a/lib/locomotive/import/asset_collections.rb b/lib/locomotive/import/asset_collections.rb index 1372e0aa..1253e80e 100644 --- a/lib/locomotive/import/asset_collections.rb +++ b/lib/locomotive/import/asset_collections.rb @@ -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 \ No newline at end of file +# 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 \ No newline at end of file diff --git a/lib/locomotive/import/content_types.rb b/lib/locomotive/import/content_types.rb index 0665671e..7355126d 100644 --- a/lib/locomotive/import/content_types.rb +++ b/lib/locomotive/import/content_types.rb @@ -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) diff --git a/lib/locomotive/import/job.rb b/lib/locomotive/import/job.rb index 5be365be..d5ab2ec0 100644 --- a/lib/locomotive/import/job.rb +++ b/lib/locomotive/import/job.rb @@ -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 diff --git a/lib/locomotive/import/pages.rb b/lib/locomotive/import/pages.rb index 3647af7b..4ebce0eb 100644 --- a/lib/locomotive/import/pages.rb +++ b/lib/locomotive/import/pages.rb @@ -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('/') diff --git a/lib/locomotive/import/snippets.rb b/lib/locomotive/import/snippets.rb index abab4f2f..2208449f 100644 --- a/lib/locomotive/import/snippets.rb +++ b/lib/locomotive/import/snippets.rb @@ -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 diff --git a/lib/tasks/locomotive.rake b/lib/tasks/locomotive.rake index 227de113..18b5a3a3 100644 --- a/lib/tasks/locomotive.rake +++ b/lib/tasks/locomotive.rake @@ -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 diff --git a/spec/fixtures/themes/default.zip b/spec/fixtures/themes/default.zip index 404afe890f792a42033c35962a9b1b6363a02df1..9d3cc5c9da21e57c0f52d96974984b7d45e7cb43 100644 GIT binary patch delta 5964 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 R1K|H6w1dhuAc#Hu^M4*#o(TW| delta 18054 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 CT(OD( diff --git a/spec/lib/locomotive/import_spec.rb b/spec/lib/locomotive/import_spec.rb index 5935e0dd..516d522b 100644 --- a/spec/lib/locomotive/import_spec.rb +++ b/spec/lib/locomotive/import_spec.rb @@ -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