Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
jsonapi-resources / test / fixtures / active_record.rb
Size: Mime:
require 'active_record'
require 'jsonapi-resources'

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.uncountable 'preferences'
  inflect.irregular 'numero_telefone', 'numeros_telefone'
end

### DATABASE
ActiveRecord::Schema.define do
  create_table :people, force: true do |t|
    t.string     :name
    t.string     :email
    t.datetime   :date_joined
    t.belongs_to :preferences
    t.integer    :hair_cut_id, index: true
    t.boolean    :book_admin, default: false
    t.boolean    :special, default: false
    t.timestamps null: false
  end

  create_table :author_details, force: true do |t|
    t.integer :person_id
    t.string  :author_stuff
  end

  create_table :posts, force: true do |t|
    t.string     :title
    t.text       :body
    t.integer    :author_id
    t.belongs_to :section, index: true
    t.timestamps null: false
  end

  create_table :comments, force: true do |t|
    t.text       :body
    t.belongs_to :post, index: true
    t.integer    :author_id
    t.timestamps null: false
  end

  create_table :companies, force: true do |t|
    t.string     :type
    t.string     :name
    t.string     :address
    t.timestamps null: false
  end

  create_table :tags, force: true do |t|
    t.string :name
  end

  create_table :sections, force: true do |t|
    t.string :name
  end

  create_table :posts_tags, force: true do |t|
    t.references :post, :tag, index: true
  end
  add_index :posts_tags, [:post_id, :tag_id], unique: true

  create_table :special_post_tags, force: true do |t|
    t.references :post, :tag, index: true
  end
  add_index :special_post_tags, [:post_id, :tag_id], unique: true

  create_table :comments_tags, force: true do |t|
    t.references :comment, :tag, index: true
  end

  create_table :iso_currencies, id: false, force: true do |t|
    t.string :code, limit: 3, null: false
    t.string :name
    t.string :country_name
    t.string :minor_unit
    t.timestamps null: false
  end
  add_index :iso_currencies, :code, unique: true

  create_table :expense_entries, force: true do |t|
    t.string :currency_code, limit: 3, null: false
    t.integer :employee_id, null: false
    t.decimal :cost, precision: 12, scale: 4, null: false
    t.date :transaction_date
  end

  create_table :planets, force: true do |t|
    t.string :name
    t.string :description
    t.integer :planet_type_id
  end

  create_table :planets_tags, force: true do |t|
    t.references :planet, :tag, index: true
  end
  add_index :planets_tags, [:planet_id, :tag_id], unique: true

  create_table :planet_types, force: true do |t|
    t.string :name
  end

  create_table :moons, force: true do |t|
    t.string  :name
    t.string  :description
    t.integer :planet_id
  end

  create_table :craters, id: false, force: true do |t|
    t.string  :code
    t.string  :description
    t.integer :moon_id
  end

  create_table :preferences, force: true do |t|
    t.integer :person_id
    t.boolean :advanced_mode, default: false
  end

  create_table :facts, force: true do |t|
    t.integer  :person_id
    t.string   :spouse_name
    t.text     :bio
    t.float    :quality_rating
    t.decimal  :salary, precision: 12, scale: 2
    t.datetime :date_time_joined
    t.date     :birthday
    t.time     :bedtime
    t.binary   :photo, limit: 1.kilobyte
    t.boolean  :cool
  end

  create_table :books, force: true do |t|
    t.string :title
    t.string :isbn
    t.boolean :banned, default: false
  end

  create_table :book_authors, force: true do |t|
    t.integer :book_id
    t.integer :person_id
  end

  create_table :book_comments, force: true do |t|
    t.text       :body
    t.belongs_to :book, index: true
    t.integer    :author_id
    t.boolean    :approved, default: true
    t.timestamps null: false
  end

  create_table :customers, force: true do |t|
    t.string   :name
  end

  create_table :purchase_orders, force: true do |t|
    t.date     :order_date
    t.date     :requested_delivery_date
    t.date     :delivery_date
    t.integer  :customer_id
    t.string   :delivery_name
    t.string   :delivery_address_1
    t.string   :delivery_address_2
    t.string   :delivery_city
    t.string   :delivery_state
    t.string   :delivery_postal_code
    t.float    :delivery_fee
    t.float    :tax
    t.float    :total
    t.timestamps null: false
  end

  create_table :order_flags, force: true do |t|
    t.string :name
  end

  create_table :purchase_orders_order_flags, force: true do |t|
    t.references :purchase_order, :order_flag, index: true
  end
  add_index :purchase_orders_order_flags, [:purchase_order_id, :order_flag_id], unique: true, name: "po_flags_idx"

  create_table :line_items, force: true do |t|
    t.integer  :purchase_order_id
    t.string   :part_number
    t.string   :quantity
    t.float    :item_cost
    t.timestamps null: false
  end

  create_table :hair_cuts, force: true do |t|
    t.string :style
  end

  create_table :numeros_telefone, force: true do |t|
    t.string   :numero_telefone
    t.timestamps null: false
  end

  create_table :categories, force: true do |t|
    t.string :name
    t.string :status, limit: 10
  end

  create_table :pictures, force: true do |t|
    t.string  :name
    t.integer :imageable_id
    t.string  :imageable_type
    t.timestamps null: false
  end

  create_table :documents, force: true do |t|
    t.string  :name
    t.timestamps null: false
  end

  create_table :products, force: true do |t|
    t.string  :name
    t.timestamps null: false
  end

  create_table :vehicles, force: true do |t|
    t.string :type
    t.string :make
    t.string :model
    t.string :length_at_water_line
    t.string :drive_layout
    t.string :serial_number
    t.integer :person_id
  end

  create_table :makes, force: true do |t|
    t.string :model
  end

  # special cases - fields that look like they should be reserved names
  create_table :hrefs, force: true do |t|
    t.string :name
  end

  create_table :links, force: true do |t|
    t.string :name
  end

  create_table :web_pages, force: true do |t|
    t.string :href
    t.string :link
  end

  create_table :questionables, force: true do |t|
  end
  # special cases
end

### MODELS
class Person < ActiveRecord::Base
  has_many :posts, foreign_key: 'author_id'
  has_many :comments, foreign_key: 'author_id'
  has_many :expense_entries, foreign_key: 'employee_id', dependent: :restrict_with_exception
  has_many :vehicles
  belongs_to :preferences
  belongs_to :hair_cut
  has_one :author_detail

  has_and_belongs_to_many :books, join_table: :book_authors

  ### Validations
  validates :name, presence: true
  validates :date_joined, presence: true
end

class AuthorDetail < ActiveRecord::Base
  belongs_to :author, class_name: 'Person', foreign_key: 'person_id'
end

class Post < ActiveRecord::Base
  belongs_to :author, class_name: 'Person', foreign_key: 'author_id'
  belongs_to :writer, class_name: 'Person', foreign_key: 'author_id'
  has_many :comments
  has_and_belongs_to_many :tags, join_table: :posts_tags
  has_many :special_post_tags, source: :tag
  has_many :special_tags, through: :special_post_tags, source: :tag
  belongs_to :section

  validates :author, presence: true
  validates :title, length: { maximum: 35 }

  before_destroy :destroy_callback

  def destroy_callback
    if title == "can't destroy me"
      errors.add(:title, "can't destroy me")

      if Rails::VERSION::MAJOR >= 5
        throw(:abort)
      else
        return false
      end
    end
  end
end

class SpecialPostTag < ActiveRecord::Base
  belongs_to :tag
  belongs_to :post
end

class Comment < ActiveRecord::Base
  belongs_to :author, class_name: 'Person', foreign_key: 'author_id'
  belongs_to :post
  has_and_belongs_to_many :tags, join_table: :comments_tags
end

class Company < ActiveRecord::Base
end

class Firm < Company
end

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :posts, join_table: :posts_tags
  has_and_belongs_to_many :planets, join_table: :planets_tags
end

class Section < ActiveRecord::Base
end

class HairCut < ActiveRecord::Base
end

class Property < ActiveRecord::Base
end

class Customer < ActiveRecord::Base
end

class BadlyNamedAttributes < ActiveRecord::Base
end

class Cat < ActiveRecord::Base
end

class IsoCurrency < ActiveRecord::Base
  self.primary_key = :code
  # has_many :expense_entries, foreign_key: 'currency_code'
end

class ExpenseEntry < ActiveRecord::Base
  belongs_to :employee, class_name: 'Person', foreign_key: 'employee_id'
  belongs_to :iso_currency, foreign_key: 'currency_code'
end

class Planet < ActiveRecord::Base
  has_many :moons
  belongs_to :planet_type

  has_and_belongs_to_many :tags, join_table: :planets_tags

  # Test model callback cancelling save
  before_save :check_not_pluto

  def check_not_pluto
    # Pluto can't be a planet, so cancel the save
    if name.downcase == 'pluto'
      # :nocov:
      if Rails::VERSION::MAJOR >= 5
        throw(:abort)
      else
        return false
      end
      # :nocov:
   end
  end
end

class PlanetType < ActiveRecord::Base
  has_many :planets
end

class Moon < ActiveRecord::Base
  belongs_to :planet

  has_many :craters
end

class Crater < ActiveRecord::Base
  self.primary_key = :code

  belongs_to :moon
end

class Preferences < ActiveRecord::Base
  has_one :author, class_name: 'Person', :inverse_of => 'preferences'
end

class Fact < ActiveRecord::Base
  validates :spouse_name, :bio, presence: true
end

class Like < ActiveRecord::Base
end

class Breed

  def initialize(id = nil, name = nil)
    if id.nil?
      @id = $breed_data.new_id
      $breed_data.add(self)
    else
      @id = id
    end
    @name = name
    @errors = ActiveModel::Errors.new(self)
  end

  attr_accessor :id, :name

  def destroy
    $breed_data.remove(@id)
  end

  def valid?
    @errors.clear
    if name.is_a?(String) && name.length > 0
      return true
    else
      @errors.add(:name, "can't be blank")
      return false
    end
  end

  def errors
    @errors
  end
end

class Book < ActiveRecord::Base
  has_many :book_comments
  has_many :approved_book_comments, -> { where(approved: true) }, class_name: "BookComment"

  has_and_belongs_to_many :authors, join_table: :book_authors, class_name: "Person"
end

class BookComment < ActiveRecord::Base
  belongs_to :author, class_name: 'Person', foreign_key: 'author_id'
  belongs_to :book

  def self.for_user(current_user)
    records = self
    # Hide the unapproved comments from people who are not book admins
    unless current_user && current_user.book_admin
      records = records.where(approved: true)
    end
    records
  end
end

class BreedData
  def initialize
    @breeds = {}
  end

  def breeds
    @breeds
  end

  def new_id
    @breeds.keys.max + 1
  end

  def add(breed)
    @breeds[breed.id] = breed
  end

  def remove(id)
    @breeds.delete(id)
  end
end

class Customer < ActiveRecord::Base
  has_many :purchase_orders
end

class PurchaseOrder < ActiveRecord::Base
  belongs_to :customer
  has_many :line_items
  has_many :admin_line_items, class_name: 'LineItem', foreign_key: 'purchase_order_id'

  has_and_belongs_to_many :order_flags, join_table: :purchase_orders_order_flags

  has_and_belongs_to_many :admin_order_flags, join_table: :purchase_orders_order_flags, class_name: 'OrderFlag'
end

class OrderFlag < ActiveRecord::Base
  has_and_belongs_to_many :purchase_orders, join_table: :purchase_orders_order_flags
end

class LineItem < ActiveRecord::Base
  belongs_to :purchase_order
end

class NumeroTelefone < ActiveRecord::Base
end

class Category < ActiveRecord::Base
end

class Picture < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true
end

class Vehicle < ActiveRecord::Base
  belongs_to :person
end

class Car < Vehicle
end

class Boat < Vehicle
end

class Document < ActiveRecord::Base
  has_many :pictures, as: :imageable
end

class Product < ActiveRecord::Base
  has_one :picture, as: :imageable
end

class Make < ActiveRecord::Base
end

class WebPage < ActiveRecord::Base
end

module Api
  module V7
    class Client < Customer
    end

    class Customer < Customer
    end
  end
end

### OperationsProcessor
class CountingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor
  after_find_operation do
    @operation_meta[:total_records] = @operation.record_count
    @operation_links['spec'] = 'https://test_corp.com'
  end
end

# This processor swaps in a mock for the operation that will raise an exception
# when it receives the :apply method. This is used to test the
# exception_class_whitelist configuration.
class ErrorRaisingOperationsProcessor < ActiveRecordOperationsProcessor
  def process_operation(operation)
    mock_operation = Minitest::Mock.new
    mock_operation.expect(:apply, true) do
      raise PostsController::SubSpecialError
    end
    super(mock_operation)
  end
end

### CONTROLLERS
class AuthorsController < JSONAPI::ResourceController
end

class PeopleController < JSONAPI::ResourceController
end

class BaseController < ActionController::Base
  include JSONAPI::ActsAsResourceController
end

class PostsController < BaseController

  class SpecialError < StandardError; end
  class SubSpecialError < PostsController::SpecialError; end

  # This is used to test that classes that are whitelisted are reraised by
  # the operations processor.
  rescue_from PostsController::SpecialError do
    head :forbidden
  end

  def handle_exceptions(e)
    case e
      when PostsController::SpecialError
        raise e
      else
        super(e)
    end
  end

  #called by test_on_server_error
  def self.set_callback_message(error)
    @callback_message = "Sent from method"
  end
end

class CommentsController < JSONAPI::ResourceController
end

class FirmsController < JSONAPI::ResourceController
end

class SectionsController < JSONAPI::ResourceController
end

class TagsController < JSONAPI::ResourceController
end

class IsoCurrenciesController < JSONAPI::ResourceController
end

class ExpenseEntriesController < JSONAPI::ResourceController
end

class BreedsController < JSONAPI::ResourceController
end

class FactsController < JSONAPI::ResourceController
end

class CategoriesController < JSONAPI::ResourceController
end

class PicturesController < JSONAPI::ResourceController
end

class DocumentsController < JSONAPI::ResourceController
end

class ProductsController < JSONAPI::ResourceController
end

class ImageablesController < JSONAPI::ResourceController
end

class VehiclesController < JSONAPI::ResourceController
end

class CarsController < JSONAPI::ResourceController
end

class BoatsController < JSONAPI::ResourceController
end

class BooksController < JSONAPI::ResourceController
end

class AuthorsController < JSONAPI::ResourceController
end

### CONTROLLERS
module Api
  module V1
    class AuthorsController < JSONAPI::ResourceController
    end

    class PeopleController < JSONAPI::ResourceController
    end

    class PostsController < ActionController::Base
      include JSONAPI::ActsAsResourceController
    end

    class TagsController < JSONAPI::ResourceController
    end

    class IsoCurrenciesController < JSONAPI::ResourceController
    end

    class ExpenseEntriesController < JSONAPI::ResourceController
    end

    class BreedsController < JSONAPI::ResourceController
    end

    class PlanetsController < JSONAPI::ResourceController
    end

    class PlanetTypesController < JSONAPI::ResourceController
    end

    class MoonsController < JSONAPI::ResourceController
    end

    class CratersController < JSONAPI::ResourceController
    end

    class LikesController < JSONAPI::ResourceController
    end
  end

  module V2
    class AuthorsController < JSONAPI::ResourceController
    end

    class PeopleController < JSONAPI::ResourceController
    end

    class PostsController < JSONAPI::ResourceController
    end

    class PreferencesController < JSONAPI::ResourceController
    end

    class BooksController < JSONAPI::ResourceController
      def context
        {current_user: $test_user}
      end
    end

    class BookCommentsController < JSONAPI::ResourceController
      def context
        {current_user: $test_user}
      end
    end
  end

  module V3
    class PostsController < JSONAPI::ResourceController
    end
  end

  module V4
    class PostsController < JSONAPI::ResourceController
    end

    class ExpenseEntriesController < JSONAPI::ResourceController
    end

    class IsoCurrenciesController < JSONAPI::ResourceController
    end

    class BooksController < JSONAPI::ResourceController
    end
  end

  module V5
    class AuthorsController < JSONAPI::ResourceController
      def serialization_options
        {foo: 'bar'}
      end
    end

    class PostsController < JSONAPI::ResourceController
    end

    class ExpenseEntriesController < JSONAPI::ResourceController
    end

    class IsoCurrenciesController < JSONAPI::ResourceController
    end
  end

  module V6
    class CustomersController < JSONAPI::ResourceController
    end

    class PurchaseOrdersController < JSONAPI::ResourceController
      def context
        {current_user: $test_user}
      end
    end

    class LineItemsController < JSONAPI::ResourceController
    end

    class OrderFlagsController < JSONAPI::ResourceController
    end
  end

  module V7
    class CustomersController < JSONAPI::ResourceController
    end

    class PurchaseOrdersController < JSONAPI::ResourceController
    end

    class LineItemsController < JSONAPI::ResourceController
    end

    class OrderFlagsController < JSONAPI::ResourceController
    end

    class CategoriesController < JSONAPI::ResourceController
    end

    class ClientsController < JSONAPI::ResourceController
    end
  end

  module V8
    class NumerosTelefoneController < JSONAPI::ResourceController
    end
  end
end

### RESOURCES
class BaseResource < JSONAPI::Resource
  abstract
end

class PersonResource < BaseResource
  attributes :name, :email
  attribute :date_joined, format: :date_with_timezone

  has_many :comments
  has_many :posts
  has_many :vehicles, polymorphic: true

  has_one :preferences
  has_one :hair_cut

  filter :name, verify: :verify_name_filter

  def self.verify_name_filter(values, _context)
    values.each do |value|
      if value.length < 3
        raise JSONAPI::Exceptions::InvalidFilterValue.new(:name, value)
      end
    end
    return values
  end

end

class SpecialBaseResource < BaseResource
  abstract

  model_hint model: Person, resource: :special_person
end

class SpecialPersonResource < SpecialBaseResource
  model_name 'Person'

  def self.records(options = {})
    Person.where(special: true)
  end
end

class VehicleResource < JSONAPI::Resource
  immutable

  has_one :person
  attributes :make, :model, :serial_number
end

class CarResource < VehicleResource
  attributes :drive_layout
end

class BoatResource < VehicleResource
  attributes :length_at_water_line
end

class CommentResource < JSONAPI::Resource
  attributes :body
  has_one :post
  has_one :author, class_name: 'Person'
  has_many :tags

  filters :body
end

class CompanyResource < JSONAPI::Resource
  attributes :name, :address
end

class FirmResource < CompanyResource
end

class TagResource < JSONAPI::Resource
  attributes :name

  has_many :posts
  # Not including the planets relationship so they don't get output
  #has_many :planets
end

class SectionResource < JSONAPI::Resource
  attributes 'name'
end

class PostResource < JSONAPI::Resource
  attribute :title
  attribute :body
  attribute :subject

  has_one :author, class_name: 'Person'
  has_one :section
  has_many :tags, acts_as_set: true
  has_many :comments, acts_as_set: false


  # Not needed - just for testing
  primary_key :id

  before_save do
    msg = "Before save"
  end

  after_save do
    msg = "After save"
  end

  before_update do
    msg = "Before update"
  end

  after_update do
    msg = "After update"
  end

  before_replace_fields do
    msg = "Before replace_fields"
  end

  after_replace_fields do
    msg = "After replace_fields"
  end

  around_update :around_update_check

  def around_update_check
    # do nothing
    yield
    # do nothing
  end

  def subject
    @model.title
  end

  def title=(title)
    @model.title = title
    if title == 'BOOM'
      raise 'The Server just tested going boom. If this was a real emergency you would be really dead right now.'
    end
  end

  filters :title, :author, :tags, :comments
  filter :id, verify: ->(values, context) {
    verify_keys(values, context)
    return values
  }
  filter :ids,
         verify: ->(values, context) {
           verify_keys(values, context)
           return values
         },
         apply: -> (records, value, _options) {
           records.where('id IN (?)', value)
         }

  filter :search,
    verify: ->(values, context) {
      values.all?{|v| (v.is_a?(Hash) || v.is_a?(ActionController::Parameters)) } && values
    },
    apply: -> (records, values, _options) {
      records.where(title: values.first['title'])
    }

  def self.updatable_fields(context)
    super(context) - [:author, :subject]
  end

  def self.creatable_fields(context)
    super(context) - [:subject]
  end

  def self.sortable_fields(context)
    super(context) - [:id]
  end

  def self.verify_key(key, context = nil)
    super(key)
    raise JSONAPI::Exceptions::RecordNotFound.new(key) unless find_by_key(key, context: context)
    return key
  end
end

class HairCutResource < JSONAPI::Resource
  attribute :style
  has_many :people
end

class IsoCurrencyResource < JSONAPI::Resource
  attributes :name, :country_name, :minor_unit

  filter :country_name

  key_type :string
end

class ExpenseEntryResource < JSONAPI::Resource
  attributes :cost
  attribute :transaction_date, format: :date

  has_one :iso_currency, foreign_key: 'currency_code'
  has_one :employee, class_name: 'Person'
end

class EmployeeResource < JSONAPI::Resource
  attributes :name, :email
  model_name 'Person'
end

class BreedResource < JSONAPI::Resource
  attribute :name, format: :title

  # This is unneeded, just here for testing
  routing_options param: :id

  def self.find(filters, options = {})
    breeds = []
    $breed_data.breeds.values.each do |breed|
      breeds.push(BreedResource.new(breed, options[:context]))
    end
    breeds
  end

  def self.find_by_key(id, options = {})
    BreedResource.new($breed_data.breeds[id.to_i], options[:context])
  end

  def _save
    super
    return :accepted
  end
end

class PlanetResource < JSONAPI::Resource
  attribute :name
  attribute :description

  has_many :moons
  has_one :planet_type

  has_many :tags, acts_as_set: true

  def records_for_moons
    Moon.joins(:craters).select('moons.*, craters.code').distinct
  end
end

class PropertyResource < JSONAPI::Resource
  attributes :name

  has_many :planets
end

class PlanetTypeResource < JSONAPI::Resource
  attributes :name
  has_many :planets
end

class MoonResource < JSONAPI::Resource
  attribute :name
  attribute :description

  has_one :planet
  has_many :craters
end

class CraterResource < JSONAPI::Resource
  attribute :code
  attribute :description

  has_one :moon

  def self.verify_key(key, context = nil)
    key && String(key)
  end
end

class PreferencesResource < JSONAPI::Resource
  attribute :advanced_mode

  has_one :author, :foreign_key_on => :related

  def self.find_by_key(key, options = {})
    new(Preferences.first, nil)
  end
end

class FactResource < JSONAPI::Resource
  attribute :spouse_name
  attribute :bio
  attribute :quality_rating
  attribute :salary
  attribute :date_time_joined
  attribute :birthday
  attribute :bedtime
  attribute :photo
  attribute :cool
end

class CategoryResource < JSONAPI::Resource
  filter :status, default: 'active'
end

class PictureResource < JSONAPI::Resource
  attribute :name
  has_one :imageable, polymorphic: true
end

class DocumentResource < JSONAPI::Resource
  attribute :name
  has_many :pictures
end

class ProductResource < JSONAPI::Resource
  attribute :name
  has_one :picture, always_include_linkage_data: true

  def picture_id
    _model.picture.id
  end
end

class ImageableResource < JSONAPI::Resource
end

class MakeResource < JSONAPI::Resource
  attribute :model
end

class WebPageResource < JSONAPI::Resource
  attribute :href
  attribute :link
end

class AuthorResource < JSONAPI::Resource
  model_name 'Person'
  attributes :name
end

class BookResource < JSONAPI::Resource
  has_many :authors, class_name: 'Author'
end

class AuthorDetailResource < JSONAPI::Resource
  attributes :author_stuff
end

class SimpleCustomLinkResource < JSONAPI::Resource
  model_name 'Post'
  attributes :title, :body, :subject

  def subject
    @model.title
  end

  has_one :writer, foreign_key: 'author_id', class_name: 'Writer'
  has_one :section
  has_many :comments, acts_as_set: false

  filters :writer

  def custom_links(options)
    { raw: options[:serializer].link_builder.self_link(self) + "/raw" }
  end
end

class CustomLinkWithRelativePathOptionResource < JSONAPI::Resource
  model_name 'Post'
  attributes :title, :body, :subject

  def subject
    @model.title
  end

  has_one :writer, foreign_key: 'author_id', class_name: 'Writer'
  has_one :section
  has_many :comments, acts_as_set: false

  filters :writer

  def custom_links(options)
    { raw: options[:serializer].link_builder.self_link(self) + "/super/duper/path.xml" }
  end
end

class CustomLinkWithIfCondition < JSONAPI::Resource
  model_name 'Post'
  attributes :title, :body, :subject

  def subject
    @model.title
  end

  has_one :writer, foreign_key: 'author_id', class_name: 'Writer'
  has_one :section
  has_many :comments, acts_as_set: false

  filters :writer

  def custom_links(options)
    if title == "JR Solves your serialization woes!"
      {conditional_custom_link: options[:serializer].link_builder.self_link(self) + "/conditional/link.json"}
    end
  end
end

class CustomLinkWithLambda < JSONAPI::Resource
  model_name 'Post'
  attributes :title, :body, :subject, :created_at

  def subject
    @model.title
  end

  has_one :writer, foreign_key: 'author_id', class_name: 'Writer'
  has_one :section
  has_many :comments, acts_as_set: false

  filters :writer

  def custom_links(options)
    {
      link_to_external_api: "http://external-api.com/posts/#{ created_at.year }/#{ created_at.month }/#{ created_at.day }-#{ subject.gsub(' ', '-') }"
    }
  end
end


module Api
  module V1
    class WriterResource < JSONAPI::Resource
      attributes :name, :email
      model_name 'Person'
      has_many :posts

      filter :name
    end

    class LikeResource < JSONAPI::Resource
    end

    class PostResource < JSONAPI::Resource
      # V1 no longer supports tags and now calls author 'writer'
      attribute :title
      attribute :body
      attribute :subject

      has_one :writer, foreign_key: 'author_id', class_name: 'Writer'
      has_one :section
      has_many :comments, acts_as_set: false

      def subject
        @model.title
      end

      filters :writer
    end

    class PersonResource < PersonResource; end
    class CommentResource < CommentResource; end
    class TagResource < TagResource; end
    class SectionResource < SectionResource; end
    class IsoCurrencyResource < IsoCurrencyResource; end
    class ExpenseEntryResource < ExpenseEntryResource; end
    class BreedResource < BreedResource; end
    class PlanetResource < PlanetResource; end
    class PlanetTypeResource < PlanetTypeResource; end
    class MoonResource < MoonResource; end
    class CraterResource < CraterResource; end
    class PreferencesResource < PreferencesResource; end
    class EmployeeResource < EmployeeResource; end
    class HairCutResource < HairCutResource; end
    class VehicleResource < VehicleResource; end
    class CarResource < CarResource; end
    class BoatResource < BoatResource; end
  end
end

module Api
  module V2
    class PreferencesResource < PreferencesResource; end
    class PersonResource < PersonResource; end
    class PostResource < PostResource; end

    class BookResource < JSONAPI::Resource
      attribute :title
      attributes :isbn, :banned

      has_many :book_comments, relation_name: -> (options = {}) {
        context = options[:context]
        current_user = context ? context[:current_user] : nil

        unless current_user && current_user.book_admin
          :approved_book_comments
        else
          :book_comments
        end
      }

      has_many :aliased_comments, class_name: 'BookComments', relation_name: :approved_book_comments

      filters :book_comments
      filter :banned, apply: :apply_filter_banned

      class << self
        def books
          Book.arel_table
        end

        def not_banned_books
          books[:banned].eq(false)
        end

        def records(options = {})
          context = options[:context]
          current_user = context ? context[:current_user] : nil

          records = _model_class
          # Hide the banned books from people who are not book admins
          unless current_user && current_user.book_admin
            records = records.where(not_banned_books)
          end
          records
        end

        def apply_filter_banned(records, value, options)
          context = options[:context]
          current_user = context ? context[:current_user] : nil

          # Only book admins might filter for banned books
          if current_user && current_user.book_admin
            records.where('books.banned = ?', value[0] == 'true')
          end
        end

      end
    end

    class BookCommentResource < JSONAPI::Resource
      attributes :body, :approved

      has_one :book
      has_one :author, class_name: 'Person'

      filters :book
      filter :approved, apply: ->(records, value, options) {
        context = options[:context]
        current_user = context ? context[:current_user] : nil

        if current_user && current_user.book_admin
          records.where(approved_comments(value[0] == 'true'))
        end
      }

      class << self
        def book_comments
          BookComment.arel_table
        end

        def approved_comments(approved = true)
          book_comments[:approved].eq(approved)
        end

        def records(options = {})
          current_user = options[:context][:current_user]
          _model_class.for_user(current_user)
        end
      end
    end
  end
end

module Api
  module V3
    class PostResource < PostResource; end
    class PreferencesResource < PreferencesResource; end
  end
end

module Api
  module V4
    class PostResource < PostResource; end
    class PersonResource < PersonResource; end
    class ExpenseEntryResource < ExpenseEntryResource; end
    class IsoCurrencyResource < IsoCurrencyResource; end

    class BookResource < Api::V2::BookResource
      paginator :paged
    end

    class BookCommentResource < Api::V2::BookCommentResource
      paginator :paged
    end
  end
end

module Api
  module V5
    class AuthorResource < JSONAPI::Resource
      attributes :name, :email
      model_name 'Person'
      relationship :posts, to: :many
      relationship :author_detail, to: :one, foreign_key_on: :related

      filter :name

      def self.find_by_key(key, options = {})
        context = options[:context]
        records = records(options)
        records = apply_includes(records, options)
        model = records.where({_primary_key => key}).first
        fail JSONAPI::Exceptions::RecordNotFound.new(key) if model.nil?
        self.new(model, context)
      end

      def self.find(filters, options = {})
        resources = []

        filters.each do |attr, filter|
          _model_class.where("\"#{attr}\" LIKE \"%#{filter[0]}%\"").each do |model|
            resources.push self.new(model, options[:context])
          end
        end
        return resources
      end

      def fetchable_fields
        super - [:email]
      end
    end

    class AuthorDetailResource < JSONAPI::Resource
      attributes :author_stuff
    end

    class PersonResource < PersonResource; end
    class PostResource < PostResource; end
    class TagResource < TagResource; end
    class SectionResource < SectionResource; end
    class CommentResource < CommentResource; end
    class ExpenseEntryResource < ExpenseEntryResource; end
    class IsoCurrencyResource < IsoCurrencyResource; end
    class EmployeeResource < EmployeeResource; end
  end
end

module Api
  module V6
    class CustomerResource < JSONAPI::Resource
      attribute :name

      has_many :purchase_orders
    end

    class PurchaseOrderResource < JSONAPI::Resource
      attribute :order_date
      attribute :requested_delivery_date
      attribute :delivery_date
      attribute :delivery_name
      attribute :delivery_address_1
      attribute :delivery_address_2
      attribute :delivery_city
      attribute :delivery_state
      attribute :delivery_postal_code
      attribute :delivery_fee
      attribute :tax
      attribute :total

      has_one :customer
      has_many :line_items, relation_name: -> (options = {}) {
                            context = options[:context]
                            current_user = context ? context[:current_user] : nil

                            unless current_user && current_user.book_admin
                              :line_items
                            else
                              :admin_line_items
                            end
                          }

      has_many :order_flags, acts_as_set: true,
               relation_name: -> (options = {}) {
                             context = options[:context]
                             current_user = context ? context[:current_user] : nil

                             unless current_user && current_user.book_admin
                               :order_flags
                             else
                               :admin_order_flags
                             end
                           }
    end

    class OrderFlagResource < JSONAPI::Resource
      attributes :name

      has_many :purchase_orders
    end

    class LineItemResource < JSONAPI::Resource
      attribute :part_number
      attribute :quantity
      attribute :item_cost

      has_one :purchase_order
    end
  end

  module V7
    class PurchaseOrderResource < V6::PurchaseOrderResource; end
    class OrderFlagResource < V6::OrderFlagResource; end
    class LineItemResource < V6::LineItemResource; end

    class CustomerResource < V6::CustomerResource
      model_name 'Api::V7::Customer'
      attribute :name
      has_many :purchase_orders
    end

    class ClientResource < JSONAPI::Resource
      model_name 'Api::V7::Customer'

      attribute :name

      has_many :purchase_orders
    end

    class CategoryResource < CategoryResource
      attribute :name

      # Raise exception for failure in controller
      def name
        fail "Something Exceptional Happened"
      end
    end
  end

  module V8
    class NumeroTelefoneResource < JSONAPI::Resource
      attribute :numero_telefone
    end
  end
end

module AdminApi
  module V1
    class PersonResource < JSONAPI::Resource
    end
  end
end

module MyEngine
  module Api
    module V1
      class PersonResource < JSONAPI::Resource
      end
    end
  end

  module AdminApi
    module V1
      class PersonResource < JSONAPI::Resource
      end
    end
  end
end

module Legacy
  class FlatPost < ActiveRecord::Base
    self.table_name = "posts"
  end
end

class FlatPostResource < JSONAPI::Resource
  model_name "Legacy::FlatPost", add_model_hint: false

  model_hint model: "Legacy::FlatPost", resource: FlatPostResource

  attribute :title
end

class FlatPostsController < JSONAPI::ResourceController
end

### PORO Data - don't do this in a production app
$breed_data = BreedData.new
$breed_data.add(Breed.new(0, 'persian'))
$breed_data.add(Breed.new(1, 'siamese'))
$breed_data.add(Breed.new(2, 'sphinx'))
$breed_data.add(Breed.new(3, 'to_delete'))