lib/wcc/contentful/model.rb in wcc-contentful-0.1.0 vs lib/wcc/contentful/model.rb in wcc-contentful-0.2.0

- old
+ new

@@ -3,22 +3,103 @@ class WCC::Contentful::Model extend WCC::Contentful::Helpers extend WCC::Contentful::ModelValidators + # The Model base class maintains a registry which is best expressed as a + # class var. + # rubocop:disable Style/ClassVars + class << self + ## + # The configured store which executes all model queries against either the + # Contentful CDN or a locally-downloaded copy. + # + # See the {sync_store}[rdoc-ref:WCC::Contentful::Configuration.sync_store] parameter + # on the WCC::Contentful::Configuration class. attr_accessor :store + + def const_missing(name) + raise WCC::Contentful::ContentTypeNotFoundError, + "Content type '#{content_type_from_constant(name)}' does not exist in the space" + end end + @@registry = {} + def self.all_models + # TODO: this needs to use the registry but it's OK for now cause we only + # use it in specs WCC::Contentful::Model.constants(false).map { |k| WCC::Contentful::Model.const_get(k) } end + ## + # Finds an Entry or Asset by ID in the configured contentful space + # and returns an initialized instance of the appropriate model type. + # + # Makes use of the configured {store}[rdoc-ref:WCC::Contentful::Model.store] + # to access the Contentful CDN. def self.find(id, context = nil) return unless raw = store.find(id) content_type = content_type_from_raw(raw) - const = WCC::Contentful::Model.const_get(constant_from_content_type(content_type)) + unless const = @@registry[content_type] + begin + # The app may have defined a model and we haven't loaded it yet + const = Object.const_missing(constant_from_content_type(content_type).to_s) + rescue NameError + nil + end + end + unless const + # Autoloading couldn't find their model - we'll register our own. + const = WCC::Contentful::Model.const_get(constant_from_content_type(content_type)) + register_for_content_type(content_type, klass: const) + end + const.new(raw, context) end + + ## + # Registers a class constant to be instantiated when resolving an instance + # of the given content type. This automatically happens for the first subclass + # of a generated model type, example: + # + # class MyMenu < WCC::Contentful::Model::Menu + # end + # + # In the above case, instances of MyMenu will be instantiated whenever a 'menu' + # content type is resolved. + # The mapping can be made explicit with the optional parameters. Example: + # + # class MyFoo < WCC::Contentful::Model::Foo + # register_for_content_type 'bar' # MyFoo is assumed + # end + # + # # in initializers/wcc_contentful.rb + # WCC::Contentful::Model.register_for_content_type('bar', klass: MyFoo) + def self.register_for_content_type(content_type = nil, klass: nil) + klass ||= self + raise ArgumentError, "#{klass} must be a class constant!" unless klass.respond_to?(:new) + content_type ||= content_type_from_constant(klass) + + @@registry[content_type] = klass + end + + ## + # Returns the current registry of content type names to constants. + def self.registry + return {} unless @@registry + @@registry.dup.freeze + end + + ## + # Checks if a content type has already been registered to a class and returns + # that class. If nil, the generated WCC::Contentful::Model::{content_type} class + # will be resolved for this content type. + def self.registered?(content_type) + @@registry[content_type] + end end + +# rubocop:enable Style/ClassVars