lib/wcc/contentful/model_builder.rb in wcc-contentful-0.2.2 vs lib/wcc/contentful/model_builder.rb in wcc-contentful-0.3.0.pre.rc

- old
+ new

@@ -1,7 +1,9 @@ # frozen_string_literal: true +require_relative './sys' + module WCC::Contentful class ModelBuilder include Helpers def initialize(types) @@ -20,84 +22,74 @@ const = typedef.name return WCC::Contentful::Model.const_get(const) if WCC::Contentful::Model.const_defined?(const) # TODO: https://github.com/dkubb/ice_nine ? typedef = typedef.deep_dup.freeze - fields = typedef.fields.keys WCC::Contentful::Model.const_set(const, Class.new(WCC::Contentful::Model) do + extend ModelSingletonMethods + include ModelMethods include Helpers + const_set('ATTRIBUTES', typedef.fields.keys.map(&:to_sym).freeze) + const_set('FIELDS', typedef.fields.keys.freeze) + + # Magic type in their system which has a separate endpoint + # but we represent in the same model space + if const == 'Asset' + define_singleton_method(:type) { 'Asset' } + else + define_singleton_method(:type) { 'Entry' } + end + define_singleton_method(:content_type) do typedef.content_type end define_singleton_method(:content_type_definition) do typedef end - define_singleton_method(:find) do |id, context = nil| - raw = WCC::Contentful::Model.store.find(id) - new(raw, context) if raw.present? - end - - define_singleton_method(:find_all) do |filter = nil, context = nil| - if filter - filter.transform_keys! { |k| k.to_s.camelize(:lower) } - bad_fields = filter.keys.reject { |k| fields.include?(k) } - raise ArgumentError, "These fields do not exist: #{bad_fields}" unless bad_fields.empty? - end - - query = WCC::Contentful::Model.store.find_all(content_type: content_type) - query = query.apply(filter) if filter - query.map { |r| new(r, context) } - end - - define_singleton_method(:find_by) do |filter, context = nil| - filter.transform_keys! { |k| k.to_s.camelize(:lower) } - bad_fields = filter.keys.reject { |k| fields.include?(k) } - raise ArgumentError, "These fields do not exist: #{bad_fields}" unless bad_fields.empty? - - result = - if defined?(context[:preview]) && context[:preview] == true - WCC::Contentful::Model.preview_store.find_by(content_type: content_type, filter: filter) - else - WCC::Contentful::Model.store.find_by(content_type: content_type, filter: filter) - end - - new(result, context) if result - end - - define_singleton_method(:inherited) do |subclass| - # only register if it's not already registered - return if WCC::Contentful::Model.registered?(typedef.content_type) - WCC::Contentful::Model.register_for_content_type(typedef.content_type, klass: subclass) - end - define_method(:initialize) do |raw, context = nil| ct = content_type_from_raw(raw) if ct != typedef.content_type raise ArgumentError, 'Wrong Content Type - ' \ "'#{raw.dig('sys', 'id')}' is a #{ct}, expected #{typedef.content_type}" end + @raw = raw.freeze - @locale = context[:locale] if context.present? - @locale ||= 'en-US' - @id = raw.dig('sys', 'id') - @space = raw.dig('sys', 'space', 'sys', 'id') - @created_at = raw.dig('sys', 'createdAt') - @created_at = Time.parse(@created_at) if @created_at.present? - @updated_at = raw.dig('sys', 'updatedAt') - @updated_at = Time.parse(@updated_at) if @updated_at.present? - @revision = raw.dig('sys', 'revision') + created_at = raw.dig('sys', 'createdAt') + created_at = Time.parse(created_at) if created_at.present? + updated_at = raw.dig('sys', 'updatedAt') + updated_at = Time.parse(updated_at) if updated_at.present? + @sys = WCC::Contentful::Sys.new( + raw.dig('sys', 'id'), + raw.dig('sys', 'type'), + raw.dig('sys', 'locale') || context.try(:[], :locale) || 'en-US', + raw.dig('sys', 'space', 'sys', 'id'), + created_at, + updated_at, + raw.dig('sys', 'revision'), + OpenStruct.new(context).freeze + ) typedef.fields.each_value do |f| - raw_value = raw.dig('fields', f.name, @locale) + raw_value = raw.dig('fields', f.name, @sys.locale) if raw_value.present? case f.type - when :DateTime - raw_value = Time.parse(raw_value).localtime + # DateTime is intentionally not parsed! + # a DateTime can be '2018-09-28', '2018-09-28T17:00:00', or '2018-09-28T17:00:00Z' + # depending entirely on the editor interface in Contentful. Trying to parse this + # requires an assumption of the correct time zone to place them in. At this point + # in the code we don't have that knowledge, so we're punting to app-defined models. + # + # As an example, a user enters '2018-09-28' into Contentful. That date is parsed as + # '2018-09-28T00:00:00Z' when system time is UTC (ex. on Heroku), but translating that + # date to US Central results in '2018-09-27' which is not what the user intentded. + # + # when :DateTime + # raw_value = Time.parse(raw_value).localtime when :Int raw_value = Integer(raw_value) when :Float raw_value = Float(raw_value) end @@ -107,15 +99,17 @@ end instance_variable_set('@' + f.name, raw_value) end end - attr_reader :id - attr_reader :space - attr_reader :created_at - attr_reader :updated_at - attr_reader :revision + attr_reader :sys + attr_reader :raw + delegate :id, to: :sys + delegate :created_at, to: :sys + delegate :updated_at, to: :sys + delegate :revision, to: :sys + delegate :space, to: :sys # Make a field for each column: typedef.fields.each_value do |f| name = f.name var_name = '@' + name @@ -123,22 +117,25 @@ when :Asset, :Link define_method(name) do val = instance_variable_get(var_name + '_resolved') return val if val.present? - return unless val = instance_variable_get(var_name) + _resolve_field(name) + end - val = - if val.is_a? Array - val.map { |v| WCC::Contentful::Model.find(v.dig('sys', 'id')) } - else - WCC::Contentful::Model.find(val.dig('sys', 'id')) - end - - instance_variable_set(var_name + '_resolved', val) - val + id_method_name = "#{name}_id" + if f.array + id_method_name = "#{name}_ids" + define_method(id_method_name) do + instance_variable_get(var_name)&.map { |link| link.dig('sys', 'id') } + end + else + define_method(id_method_name) do + instance_variable_get(var_name)&.dig('sys', 'id') + end end + alias_method id_method_name.underscore, id_method_name when :Coordinates define_method(name) do val = instance_variable_get(var_name) OpenStruct.new(val.slice('lat', 'lon')) if val end @@ -160,9 +157,10 @@ else define_method(name) do instance_variable_get(var_name) end end + alias_method name.underscore, name end end) end end