{"id":26197274,"url":"https://github.com/mongoid/mongoid-cached-json","last_synced_at":"2025-04-07T11:04:57.542Z","repository":{"id":2497857,"uuid":"3472318","full_name":"mongoid/mongoid-cached-json","owner":"mongoid","description":"Effective caching for nested JSON models.","archived":false,"fork":false,"pushed_at":"2024-01-04T17:45:12.000Z","size":167,"stargazers_count":73,"open_issues_count":10,"forks_count":13,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-31T09:05:36.024Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mongoid.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-02-17T17:53:20.000Z","updated_at":"2024-10-19T18:21:39.000Z","dependencies_parsed_at":"2024-06-21T03:52:20.204Z","dependency_job_id":"92fec3cb-0530-4e01-96bb-3ddd674664a4","html_url":"https://github.com/mongoid/mongoid-cached-json","commit_stats":{"total_commits":127,"total_committers":7,"mean_commits":"18.142857142857142","dds":"0.12598425196850394","last_synced_commit":"1586e744e1e457a64086aad1a478d58e4eea02ba"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongoid%2Fmongoid-cached-json","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongoid%2Fmongoid-cached-json/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongoid%2Fmongoid-cached-json/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongoid%2Fmongoid-cached-json/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mongoid","download_url":"https://codeload.github.com/mongoid/mongoid-cached-json/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247640462,"owners_count":20971557,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-12T02:25:17.948Z","updated_at":"2025-04-07T11:04:57.508Z","avatar_url":"https://github.com/mongoid.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Mongoid::CachedJson\n===================\n\n[![Gem Version](https://badge.fury.io/rb/mongoid-cached-json.svg)](http://badge.fury.io/rb/mongoid-cached-json)\n[![Build Status](https://travis-ci.org/mongoid/mongoid-cached-json.svg?branch=master)](https://travis-ci.org/mongoid/mongoid-cached-json)\n[![Code Climate](https://codeclimate.com/github/mongoid/mongoid-cached-json.svg)](https://codeclimate.com/github/mongoid/mongoid-cached-json)\n\nTypical `as_json` definitions may involve lots of database point queries and method calls. When returning collections of objects, a single call may yield hundreds of database queries that can take seconds. This library mitigates the problem by implementing a module called `CachedJson`.\n\n`CachedJson` enables returning multiple JSON formats and versions from a single class and provides some rules for yielding embedded or referenced data. It then uses a scheme where fragments of JSON are cached for a particular (class, id) pair containing only the data that doesn't involve references/embedded documents. To get the full JSON for an instance, `CachedJson` will combine fragments of JSON from the instance with fragments representing the JSON for its references. In the best case, when all of these fragments are cached, this falls through to a few cache lookups followed by a couple Ruby hash merges to create the JSON.\n\nUsing `Mongoid::CachedJson` we were able to cut our JSON API average response time by about a factor of 10.\n\nCompatibility\n-------------\n\nThis gem is compatible with Mongoid 3, 4, 5, 6, and 7.\n\nResources\n---------\n\n* [Need Help? Google Group](http://groups.google.com/group/mongoid-cached-json)\n* [Source Code](http://github.com/mongoid/mongoid-cached-json)\n\nQuickstart\n----------\n\nAdd `mongoid-cached-json` to your Gemfile.\n\n    gem 'mongoid-cached-json'\n\nInclude `Mongoid::CachedJson` in your models.\n\n``` ruby\nclass Gadget\n  include Mongoid::CachedJson\n\n  field :name\n  field :extras\n\n  belongs_to :widget\n\n  json_fields \\\n    name: {},\n    extras: { properties: :public }\n\nend\n\nclass Widget\n  include Mongoid::CachedJson\n\n  field :name\n  has_many :gadgets\n\n  json_fields \\\n    name: {},\n    gadgets: { type: :reference, properties: :public }\n\nend\n```\n\nInvoke `as_json`.\n\n``` ruby\nwidget = Widget.first\n\n# the `:short` version of the JSON, `gadgets` not included\nwidget.as_json\n\n# equivalent to the above\nwidget.as_json(properties: :short)\n\n# `:public` version of the JSON, `gadgets` returned with `:short` JSON, no `:extras`\nwidget.as_json(properties: :public)\n\n# `:all` version of the JSON, `gadgets` returned with `:all` JSON, including `:extras`\nwidget.as_json(properties: :all)\n```\n\nConfiguration\n-------------\n\nBy default `Mongoid::CachedJson` will use an instance of `ActiveSupport::Cache::MemoryStore` in a non-Rails and `Rails.cache` in a Rails environment. You can configure it to use any other cache store.\n\n``` ruby\nMongoid::CachedJson.configure do |config|\n  config.cache = ActiveSupport::Cache::FileStore.new\nend\n```\n\nThe default JSON version returned from `as_json` is `:unspecified`. If you wish to redefine this, set `Mongoid::CachedJson.config.default_version`.\n\n``` ruby\nMongoid::CachedJson.configure do |config|\n  config.default_version = :v2\nend\n```\n\nDefining Fields\n---------------\n\n`Mongoid::CachedJson` supports the following options:\n\n* `:hide_as_child_json_when` is an optional function that hides the child JSON from `as_json` parent objects, eg. `cached_json hide_as_child_json_when: lambda { |instance| ! instance.secret? }`\n\n`Mongoid::CachedJson` field definitions support the following options:\n\n* `:definition` can be a symbol or an anonymous function, eg. `description: { definition: :name }` or `description: { definition: lambda { |instance| instance.name } }`\n* `:type` can be `:reference`, required for referenced objects\n* `:properties` can be one of `:short`, `:public`, `:all`, in this order\n* `:version` can be a single version for this field to appear in\n* `:versions` can be an array of versions for this field to appear in\n* `:reference_properties` can be one of `:short`, `:public`, `:all`, default will select the reference properties format dynamically (see below)\n\nReference Properties\n--------------------\n\nWhen calling `as_json` on a model that contains references to other models the value of the `:properties` option passed into the `as_json` call will be chosen as follows:\n\n* Use the value of the `:reference_properties` option, if specified.\n* For `:short` JSON, use `:short`.\n* For `:public` JSON, use `:public`.\n* For `:all` JSON, use `:all`.\n\nThe dynamic selection where `:public` generates `:short` references allows to return smaller embedded collections, while `:all` allows to fetch deep data. Another way of looking at this is to say that a field in a `:short` JSON appears in collections, a field declared in the `:public` JSON appears for all users and the field declared in the `:all` JSON appears for object owners only.\n\nTo override this behavior and always return the `:short` JSON for a child reference, use `:reference_properties`. In the following example we would want `Person.as_json(properties: :all)` to return the social security number for that person, but not for all their friends.\n\n``` ruby\nclass Person\n  include Mongoid::Document\n  include Mongoid::CachedJson\n\n  field :name\n  field :ssn\n  has_and_belongs_to_many :friends, class_name: 'Person'\n\n  json_fields \\\n    name: {},\n    ssn: { properties: :all },\n    friends: { properties: :public, reference_properties: :short }\n\nend\n```\n\nVersioning\n----------\n\nYou can set an optional `version` or `versions` attribute on JSON fields. Consider the following definition where the first version defined `:name`, then split it into `:first`, `:middle` and `:last` in version `:v2` and introduced a date of birth in `:v3`.\n\n``` ruby\nclass Person\n  include Mongoid::Document\n  include Mongoid::CachedJson\n\n  field :first\n  field :last\n\n  def name\n    [ first, middle, last ].compact.join(' ')\n  end\n\n  json_fields \\\n    first: { versions: [ :v2, :v3 ] },\n    last: { versions: [ :v2, :v3 ] },\n    middle: { versions: [ :v2, :v3 ] },\n    born: { versions: :v3 },\n    name: { definition: :name }\n\nend\n```\n\n``` ruby\nperson = Person.create(first: 'John', middle: 'F.', last: 'Kennedy', born: 'May 29, 1917')\nperson.as_json # { name: 'John F. Kennedy' }\nperson.as_json(version: :v2) # { first: 'John', middle: 'F.', last: 'Kennedy', name: 'John F. Kennedy' }\nperson.as_json(version: :v3) # { first: 'John', middle: 'F.', last: 'Kennedy', name: 'John F. Kennedy', born: 'May 29, 1917' }\n```\n\nTransformations\n---------------\n\nYou can define global transformations on all JSON values with `Mongoid::CachedJson.config.transform`. Each transformation must return a value. In the following example we extend the JSON definition with an application-specific `:trusted` field and encode any content that is not trusted.\n\n``` ruby\nclass Widget\n  include Mongoid::Document\n  include Mongoid::CachedJson\n\n  field :name\n  field :description\n\n  json_fields \\\n    name: { trusted: true },\n    description: {}\nend\n```\n\n``` ruby\n  Mongoid::CachedJson.config.transform do |field, definition, value|\n    trusted = !!definition[:trusted]\n    trusted ? value : CGI.escapeHTML(value)\n  end\n```\n\nMixing with Standard as_json\n----------------------------\n\nTaking part in the `Mongoid::CachedJson` `json_fields` scheme is optional: you can still write `as_json` methods where it makes sense.\n\nTurning Caching Off\n-------------------\n\nYou can set `Mongoid::CachedJson.config.disable_caching = true`. It may be a good idea to set it to `ENV['DISABLE_JSON_CACHING']`, in case this turns out not to be The Solution To All Of Your Performance Problems (TM).\n\nTesting JSON\n------------\n\nThis library overrides `as_json`, hence testing JSON results can be done at model level.\n\n``` ruby\ndescribe 'as_json' do\n  before :each do\n    @person = Person.create!(first: 'John', last: 'Doe')\n  end\n  it 'returns name' do\n    expect(@person.as_json(properties: :public)[:name]).to eql 'John Doe'\n  end\nend\n```\n\nIt's also common to test the results of the API using the [Pathy](https://github.com/twoism/pathy) library.\n\n``` ruby\ndescribe 'as_json' do\n  before :each do\n    person = Person.create!(first: 'John', last: 'Doe')\n  end\n  it 'returns name' do\n    get \"/api/person/#{person.id}\"\n    expect(response.body.at_json_path('name')).to eql 'John Doe'\n  end\nend\n```\n\nTesting Cache Invalidation\n--------------------------\n\nCache is invalidated by calling `:expire_cached_json` on an instance.\n\n``` ruby\ndescribe 'updating a person' do\n  before :each\n    @person = Person.create!(name: 'John Doe')\n  end\n  it 'invalidates cache' do\n    expect(@person).to receive(:expire_cached_json)\n    @person.update_attributes!(name: 'updated')\n  end\nend\n```\nYou may also want to use [this RSpec matcher](spec/support/matchers/invalidate.rb).\n\n```ruby\ndescribe 'updating a person' do\n  it 'invalidates cache' do\n    expect do\n      @person.update_attributes!(name: 'updated')\n    end.to invalidate @person\n  end\nend\n```\n\nPerformance\n-----------\n\nThis gem implements two interesting optimizations.\n\n### Bulk Reference Resolving w/ Local Store\n\nConsider an array of Mongoid instances, each with numerous references to other objects. It's typical to see such instances reference the same object. `Mongoid::CachedJson` first collects all JSON references, then resolves them after suppressing duplicates. This significantly reduces the number of cache queries.\n\n### Fetching Cache Data in Bulk\n\nVarious cache stores, including Memcached, support bulk read operations. The [Dalli](https://github.com/mperham/dalli) gem exposes this via the `read_multi` method. `Mongoid::CachedJson` will always invoke `read_multi` where available, which significantly reduces the number of network roundtrips to the cache servers.\n\nContributing\n------------\n\nSee [CONTRIBUTING](CONTRIBUTING.md).\n\nCopyright and License\n---------------------\n\nMIT License, see [LICENSE](https://github.com/mongoid/mongoid-cached-json/blob/master/LICENSE.md) for details.\n\n(c) 2012-2014 [Artsy](https://artsy.net) and [Contributors](CHANGELOG.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmongoid%2Fmongoid-cached-json","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmongoid%2Fmongoid-cached-json","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmongoid%2Fmongoid-cached-json/lists"}