lib/jsonapi/resource.rb in jsonapi-resources-0.1.1 vs lib/jsonapi/resource.rb in jsonapi-resources-0.2.0

- old
+ new

@@ -70,16 +70,10 @@ change :replace_has_many_links do _replace_has_many_links(association_type, association_key_values) end end - def create_has_one_link(association_type, association_key_value) - change :create_has_one_link do - _create_has_one_link(association_type, association_key_value) - end - end - def replace_has_one_link(association_type, association_key_value) change :replace_has_one_link do _replace_has_one_link(association_type, association_key_value) end end @@ -146,23 +140,10 @@ send("#{association.foreign_key}=", association_key_values) @save_needed = true end - def _create_has_one_link(association_type, association_key_value) - association = self.class._associations[association_type] - - # ToDo: Add option to skip relations that already exist instead of returning an error? - relation = @model.send("#{association.foreign_key}") - if relation.nil? - send("#{association.foreign_key}=", association_key_value) - else - raise JSONAPI::Exceptions::HasOneRelationExists.new - end - @save_needed = true - end - def _replace_has_one_link(association_type, association_key_value) association = self.class._associations[association_type] send("#{association.foreign_key}=", association_key_value) @save_needed = true @@ -213,19 +194,21 @@ base._allowed_filters = (_allowed_filters || Set.new).dup type = base.name.demodulize.sub(/Resource$/, '').underscore base._type = type.pluralize.to_sym + base.attribute :id, format: :id + check_reserved_resource_name(base._type, base.name) # If eager loading is on this is how all the resource types are setup # If eager loading is off some resource types will be initialized in # _resource_name_from_type @@resource_types[base._type] ||= base.name.demodulize end - attr_accessor :_attributes, :_associations, :_allowed_filters , :_type + attr_accessor :_attributes, :_associations, :_allowed_filters , :_type, :_paginator def create(context) self.new(self.create_model, context) end @@ -249,10 +232,11 @@ end def attribute(attr, options = {}) check_reserved_attribute_name(attr) + @_attributes ||= {} @_attributes[attr] = options define_method attr do @model.send(attr) end unless method_defined?(attr) @@ -296,11 +280,11 @@ @_primary_key = key.to_sym end # Override in your resource to filter the updateable keys def updateable_fields(context = nil) - _updateable_associations | _attributes.keys + _updateable_associations | _attributes.keys - [_primary_key] end # Override in your resource to filter the createable keys def createable_fields(context = nil) _updateable_associations | _attributes.keys @@ -313,38 +297,55 @@ def fields _associations.keys | _attributes.keys end + def apply_pagination(records, paginator) + if paginator + records = paginator.apply(records) + end + records + end + + def apply_sort(records, order_options) + records.order(order_options) + end + def apply_filter(records, filter, value) records.where(filter => value) end - # Override this method if you have more complex requirements than this basic find method provides - def find(filters, options = {}) - context = options[:context] - sort_params = options.fetch(:sort_params) { [] } - includes = [] - - records = records(options) - + def apply_filters(records, filters) + required_includes = [] filters.each do |filter, value| if _associations.include?(filter) if _associations[filter].is_a?(JSONAPI::Association::HasMany) - includes.push(filter) + required_includes.push(filter) records = apply_filter(records, "#{filter}.#{_associations[filter].primary_key}", value) else records = apply_filter(records, "#{_associations[filter].foreign_key}", value) end else records = apply_filter(records, filter, value) end end + records.includes(required_includes) + end + # Override this method if you have more complex requirements than this basic find method provides + def find(filters, options = {}) + context = options[:context] + sort_criteria = options.fetch(:sort_criteria) { [] } + resources = [] - order_options = construct_order_options(sort_params) - records.order(order_options).includes(includes).each do |model| + + records = records(options) + records = apply_filters(records, filters) + records = apply_sort(records, construct_order_options(sort_criteria)) + records = apply_pagination(records, options[:paginator]) + + records.each do |model| resources.push self.new(model, context) end return resources end @@ -478,10 +479,18 @@ @@resource_types[type] = class_name end return class_name end + def _paginator + @_paginator ||= JSONAPI.configuration.default_paginator + end + + def paginator(paginator) + @_paginator = paginator + end + # :nocov: if RUBY_VERSION >= '2.0' def _model_class @model ||= Object.const_get(_model_name.to_s) end @@ -498,12 +507,17 @@ def module_path @module_path ||= self.name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').downcase : '' end - private + def construct_order_options(sort_params) + sort_params.each_with_object({}) { |sort, order_hash| + order_hash[sort[:field]] = sort[:direction] + } + end + private def check_reserved_resource_name(type, name) if [:ids, :types, :hrefs, :links].include?(type) warn "[NAME COLLISION] `#{name}` is a reserved resource name." return end @@ -549,33 +563,30 @@ associated_model = @model.send attr return associated_model ? resource_class.new(associated_model, @context) : nil end end unless method_defined?(attr) elsif @_associations[attr].is_a?(JSONAPI::Association::HasMany) - define_method attr do + define_method attr do |options = {}| type_name = self.class._associations[attr].type.to_s resource_class = self.class.resource_for(self.class.module_path + type_name) + filters = options.fetch(:filters, {}) + sort_criteria = options.fetch(:sort_criteria, {}) + paginator = options.fetch(:paginator, nil) + resources = [] if resource_class - associated_models = @model.send attr - associated_models.each do |associated_model| - resources.push resource_class.new(associated_model, @context) + records = @model.send attr + records = self.class.apply_filters(records, filters) + records = self.class.apply_sort(records, self.class.construct_order_options(sort_criteria)) + records = self.class.apply_pagination(records, paginator) + records.each do |record| + resources.push resource_class.new(record, @context) end end return resources end unless method_defined?(attr) end end - end - - def construct_order_options(sort_params) - sort_params.each_with_object({}) { |sort_key, order_hash| - if sort_key.starts_with?('-') - order_hash[sort_key.slice(1..-1)] = :desc - else - order_hash[sort_key] = :asc - end - } end end end end