lib/wcc/contentful/model.rb in wcc-contentful-0.2.2 vs lib/wcc/contentful/model.rb in wcc-contentful-0.3.0.pre.rc
- old
+ new
@@ -1,69 +1,103 @@
-
# frozen_string_literal: true
+# This is the top layer of the WCC::Contentful gem. It exposes an API by which
+# you can query for data from Contentful. The API is only accessible after calling
+# WCC::Contentful.init!
+#
+# The WCC::Contentful::Model class is the base class for all auto-generated model
+# classes. A model class represents a content type inside Contentful. For example,
+# the "page" content type is represented by a class named WCC::Contentful::Model::Page
+#
+# This WCC::Contentful::Model::Page class exposes the following API methods:
+# * {WCC::Contentful::ModelSingletonMethods#find Page.find(id)}
+# finds a single Page by it's ID
+# * {WCC::Contentful::ModelSingletonMethods#find_by Page.find_by(field: <value>)}
+# finds a single Page with the matching value for the specified field
+# * {WCC::Contentful::ModelSingletonMethods#find_all Page.find_all(field: <value>)}
+# finds all instances of Page with the matching value for the specified field.
+# It returns a lazy iterator of Page objects.
+#
+# The returned objects are instances of WCC::Contentful::Model::Page, or whatever
+# constant exists in the registry for the page content type. You can register
+# custom types to be instantiated for each content type. If a Model is subclassed,
+# the subclass is automatically registered. This allows you to put models in your
+# app's `app/models` directory:
+#
+# class Page < WCC::Contentful::Model::Page; end
+#
+# and then use the API via those models:
+#
+# # this returns a ::Page, not a WCC::Contentful::Model::Page
+# Page.find_by(slug: 'foo')
+#
+# Furthermore, anytime links are automatically resolved, the registered classes will
+# be used:
+#
+# Menu.find_by(name: 'home').buttons.first.linked_page # is a ::Page
+#
+# @api Model
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
- attr_accessor :preview_store
+ include WCC::Contentful::ServiceAccessors
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]
+ # Makes use of the {WCC::Contentful::Services#store configured store}
# to access the Contentful CDN.
def self.find(id, context = nil)
return unless raw = store.find(id)
+ new_from_raw(raw, context)
+ end
+
+ # Creates a new initialized instance of the appropriate model type for the
+ # given raw value. The raw value must be the same format as returned from one
+ # of the stores for a given object.
+ def self.new_from_raw(raw, context = nil)
content_type = content_type_from_raw(raw)
+ const = resolve_constant(content_type)
+ const.new(raw, context)
+ end
- 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
+ # Accepts a content type ID as a string and returns the Ruby constant
+ # stored in the registry that represents this content type.
+ def self.resolve_constant(content_type)
+ const = @@registry[content_type]
+ return const if const
+
+ const_name = constant_from_content_type(content_type).to_s
+ begin
+ # The app may have defined a model and we haven't loaded it yet
+ const = Object.const_missing(const_name)
+ return const if const && const < WCC::Contentful::Model
+ rescue NameError => e
+ raise e unless e.message =~ /uninitialized constant #{const_name}/
+
+ nil
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)
+ # 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
- ##
# 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
@@ -80,22 +114,22 @@
# # 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]