require 'thor' module Scrivito class AttributeDefinitionMigrator include Thor::Actions include Thor::Base include Thor::Shell def initialize(path) self.destination_root = Rails.root.to_s self.options = {} @path = path.to_s end def self.migrate_application Dir[Rails.root + 'app/models/**/*.rb'].each do |model_path| new(model_path).migrate end check_abandoned_obj_classes end def migrate relative_path = path.gsub("#{Rails.root.to_s}/", '') if model_class && (page_model? || widget_model?) say_status :inspecting, relative_path if obj_class say_status :migrating, relative_path inject_attribute_definitions inject_hide_from_editor if hidden_from_editor? check_model_name else with_padding do say %{[warning] Model "#{model_class}" has no corresponding ObjClass in published workspace. Ignore this message if it is an abstract class and should not have an ObjClass by design. Or maybe "#{model_class}" is not used anymore and file #{relative_path} can be removed? }, :yellow end say_status :skipping, relative_path end puts end end private attr_reader :path def model_class @model_class ||= class_name.constantize rescue NameError end def page_model? model_class.ancestors.include?(BasicObj) && model_class != ::Obj end def widget_model? model_class.ancestors.include?(BasicWidget) && model_class != ::Widget end def inject_hide_from_editor inject_into_class(path, model_class, " hide_from_editor\n\n") end def inject_attribute_definitions inject_into_class(path, model_class, "#{attribute_definitions.join}\n", verbose: false) end def hidden_from_editor? !obj_class.active? || (page_model? && !template_exists?) end def template_exists? lookup_context.template_exists?("#{class_name.underscore}/thumbnail") end def class_name @class_name ||= path.gsub(/^.*\/app\/models/, '').gsub('.rb', '').classify end def attribute_definitions definitions = obj_class_attributes.map do |attr| attribute_definition = " attribute :#{attr.name}, :#{type_for(attr.name, attr.type)}" if attr.type == 'enum' || attr.type == 'multienum' attribute_definition << ", values: #{attr.values.to_s}" end attribute_definition << "\n" end if page_model? && obj_class.legacy_type? definitions << " attribute :title, :string\n" if obj_class.is_binary definitions << " attribute :blob, :binary\n" else definitions << " attribute :body, :html\n" end end definitions.uniq end def type_for(name, type) case type when 'text' then 'string' when 'widget' then 'widgetlist' else type end end def obj_class_attributes Workspace.published.as_current do obj_class.attributes.sort_by(&:name) end end def lookup_context ActionView::LookupContext.new(ActionController::Base.view_paths) end def obj_class @obj_class = ObjClass.find(model_class.name) rescue Scrivito::ResourceNotFound end def check_model_name name = model_class.name if page_model? && !name.ends_with?('Page') is_binary = if obj_class.legacy_type? obj_class.binary? else obj_class.attributes.any? { |attr| attr.name == 'blob' && attr.type == 'binary' } end unless is_binary with_padding do say %{[warning] The class name of the non-binary obj model "#{name}" does not end with "Page". Class names of page models should end with "Page" in order to be automatically identified by Scrivito as pages. Please ignore this warning if "#{name}" is not a page by design. Otherwise you need to tell Scrivito about this page explicitly. Please add following to an initializer: Scrivito.models.define { page "#{name}" } }, :yellow end end end if widget_model? && !name.ends_with?('Widget') && Scrivito.models.widgets.none? { |w| w.name == name } with_padding do say %{[warning] The class name of the widget model "#{name}" does not end with "Widget". Class names of widget models should end with "Widget" in order to be automatically identified by Scrivito as widgets. You need to tell Scrivito about this widget explicitly. Please add following to an initializer: Scrivito.models.define { widget "#{name}" } }, :yellow end end end class << self include Thor::Actions include Thor::Base include Thor::Shell private def check_abandoned_obj_classes ObjClass.all.each do |obj_class| next if obj_class.name == 'MigrationStore' begin obj_class.name.constantize rescue NameError with_padding do say %{[warning] Could not find a ruby class for ObjClass "#{obj_class.name}". Plase create a ruby class for that ObjClass and run this script again. }, :yellow end end end end end end end