0.01
The project is in a healthy, maintained state
Ruby bindings for the Sanity API
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

Sanity

Maintainability

The Sanity Ruby library provides convenient access to the Sanity API from applications written in Ruby. It includes a pre-defined set of classes for API resources.

The library also provides other features, like:

  • Easy configuration for fast setup and use.
  • A pre-defined class to help make any PORO a "sanity resource"
  • Extensibility in overriding the serializer for the API response results
  • A small DSL around GROQ queries

Contents

  • Sanity
    • Contents
    • Getting Started
    • Serialization
    • Mutating
    • Querying
    • Development
    • Contributing
    • License

Getting Started

Add this line to your application's Gemfile:

gem 'sanity-ruby'

Setup your configuration. If using in Rails, consider setting this in an initializer:

# config/initializers/sanity.rb
Sanity.configure do |s|
  s.token = "yoursupersecrettoken"
  s.api_version = "v2021-03-25"
  s.project_id = "1234"
  s.dataset = "development"
  s.use_cdn = false
end

# OR

# Sanity.configure do |s|
#   s.token = ENV.fetch("SANITY_TOKEN", "")
#   s.api_version = ENV.fetch("SANITY_API_VERSION", "")
#   s.project_id = ENV.fetch("SANITY_PROJECT_ID", "")
#   s.dataset = ENV.fetch("SANITY_DATASET", "")
#   s.use_cdn = ENV.fetch("SANITY_USE_CDN", false)
# end

or you can set the following ENV variables at runtime without any initializer:

SANITY_TOKEN="yoursupersecrettoken"
SANITY_API_VERSION="v2021-03-25"
SANITY_PROJECT_ID="1234"
SANITY_DATASET="development"
SANITY_USE_CDN="false"

The configuration object is thread safe by default meaning you can connect to multiple different projects and/or API variations across any number of threads. A real world scenario when working with Sanity may require that you sometimes interact with the CDN based API and sometimes the non-CDN based API. Using ENV variables combined with the thread safe configuration object gives you the ultimate flexibility.

If you're using this gem in a Rails application AND you're interacting with only ONE set of configuration you can make the gem use the global configuration by setting the use_global_config option to true.

Your initializer config/initializers/sanity.rb should look like:

# `use_global_config` is NOT thread safe. DO NOT use if you intend on changing the
# config object at anytime within your application's lifecycle.
#
# Do not use `use_global_config` in your application if you're:
# - Interacting with various Sanity project ids/token
# - Interacting with multiple API versions
# - Interacting with calls that sometimes require the use of the CDN and sometimes don't

Sanity.use_global_config = true
Sanity.configure do |s|
  s.token = "yoursupersecrettoken"
  s.api_version = "v2021-03-25"
  s.project_id = "1234"
  s.dataset = "development"
  s.use_cdn = false
end

To create a new document:

Sanity::Document.create(params: {_type: "user", first_name: "Carl", last_name: "Sagan"})

You can also return the created document ID.

res = Sanity::Document.create(params: {_type: "user", first_name: "Carl", last_name: "Sagan"}, options: {return_ids: true})

# JSON.parse(res.body)["results"]
# > [{"id"=>"1fc471c6434fdc654ba447", "operation"=>"create"}]

To create a new asset:

# TODO

To make any PORO a sanity resource:

class User < Sanity::Resource
  attribute :_id, default: ""
  attribute :_type, default: ""
  mutatable only: %i(create delete)
  queryable
  publishable
end

Since Sanity::Resource includes ActiveModel::Model and ActiveModel::Attributes, you're able to define types on attributes and use methods like alias_attribute.

class User < Sanity::Resource
  ...
  attribute :name, :string, default: 'John Doe'
  attribute :_createdAt, :datetime
  alias_attribute :created_at, :_createdAt
  ...
end

To create a new document in Sanity:

User.create(params: { first_name: "Carl", last_name: "Sagan" })

or if you need to validate the object in your application first:

user = User.new(first_name: "Carl", last_name: "Sagan")
# your business logic here...
user.create

To make any PORO act like a sanity resource:

class User
  include Sanity::Mutatable
  include Sanity::Queryable
  queryable
  mutatable
end

Serialization

When using a PORO, you can opt-in to automatically serialize your results. You must define all attributes that should be serialized.

class User < Sanity::Resource
  auto_serialize
  ...
end

Additionally, you can configure a custom serializer. See how to define a custom serializer below.

class User < Sanity::Resource
  serializer UserSerializer
  ...
end

Finally, at query time you can also pass in a serializer. A serializer specified at query time will take priority over any other configuration.

User.where(active: true, serializer: UserSerializer)

where UserSerializer might look like:

class UserSerializer
  class << self
    def call(...)
      new(...).call
    end
  end

  attr_reader :results

  def initialize(args)
    @results = args["result"]
  end

  def call
    results.map do |result|
      User.new(
        _id: result["_id"],
        _type: result["_type"]
      )
    end
  end
end

Mutating

To create a document:

Sanity::Document.create(params: {_type: "user", first_name: "Carl", last_name: "Sagan"})

To create or replace a document:

Sanity::Document.create_or_replace(params: { _id: "1234-321", _type: "user", first_name: "Carl", last_name: "Sagan"})

To create a document if it does not exist:

Sanity::Document.create_if_not_exists(params: { _id: "1234-321", _type: "user", first_name: "Carl", last_name: "Sagan"})

To delete a document:

Sanity::Document.delete(params: { _id: "1234-321"})

To patch a document:

Sanity::Document.patch(params: { _id: "1234-321", set: { first_name: "Carl" }})

Publishing

To publish a document:

Sanity::Document.publish(["1234-321"])

To unpublish a document:

Sanity::Document.unpublish(["1234-321", "1432432-545"])

Querying

To find document(s) by id:

Sanity::Document.find(id: "1234-321")

To find documents based on certain fields:

Where

majority supported

where: {
  _id: "123", # _id == '123'
  _id: {not: "123"} # _id != '123'
  title: {match: "wo*"} # title match 'wo*'
  popularity: {gt: 10}, # popularity > 10
  popularity: {gt_eq: 10}, # popularity >= 10
  popularity: {lt: 10}, # popularity < 10
  popularity: {lt_eq: 10}, # popularity <= 10
  _type: "movie", or: {_type: "cast"} # _type == 'movie' || _type == 'cast'
  _type: "movie", and: {or: [{_type: "cast"}, {_type: "person"}]} # _type == 'movie' && (_type == 'cast' || _type == 'person')
  _type: "movie", or: [{_type: "cast"}, {_type: "person"}] # _type == 'movie' || _type == 'cast' || _type == 'person'
}
Sanity::Document.where(_type: "user", and: {or: {_id:  "123", first_name: "Carl" }})
# Resulting GROQ:
# *[_type == 'user' && (_id == '123' || first_name == 'Carl')]

Order

partially supported

order: { createdAt: :desc, updatedAt: :asc }
# order(createdAt desc) | order(updatedAt asc)

Limit

limit: 5, offset: 10
Sanity::Document.where(_type: "user", limit: 5, offset: 2)

Select

partially supported

select: [:_id, :slug, :title, :name]
Sanity::Document.where(_type: "user", select: %i[first_name last_name])

Should you need more advanced querying that isn't handled in this gem's DSL you can pass a raw groq query

Query Cheat Sheet

groq_query = <<-GROQ
  *[ _type =='movie' && name == $name] {
    title,
    poster {
      asset-> {
        path,
        url
      }
    }
  }
GROQ

Sanity::Document.where(groq: groq_query, variables: {name: "Monsters, Inc."})

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.

Testing across all supported versions:

To run tests across all gem supported ruby versions (requires Docker):

bin/dev-test

To run lint across all gem supported ruby versions (requires Docker):

bin/dev-lint

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/dvmonroe/sanity-ruby.

License

The gem is available as open source under the terms of the MIT License.