amatsuda / jb

A simple and fast JSON API template engine for Ruby on Rails
MIT License
1.29k stars 43 forks source link

TypeError: singleton can't be dumped #45

Closed chadwilken closed 1 year ago

chadwilken commented 1 year ago

Just wanted to say that I really enjoy the library. Thank you for all of the work that you have put in!

I am attempting to render an object to JSON using your gem and Rails 7.1. I am using Rails.cache.fetch in the jb file and am getting the error below.

A few interesting things. If I disable caching it works (that isn't feasible though). If I remove rendering additional partials it also works. This leads me to believe it has something to do with the call to render(partial: …).

Let me how I can help or if you need additional information.

# ./app/views/showcase_widget/api/showcase_projects/index.json.jb:3:in `_app_views_showcase_widget_api_showcase_projects_index_json_jb__984211662139926002_220520'
      # /bundle/ruby/3.2.0/gems/jb-0.8.1/lib/jb/action_view_monkeys.rb:19:in `_run'
      # /bundle/ruby/3.2.0/gems/jb-0.8.1/lib/jb/action_view_monkeys.rb:9:in `render_template'
      # /bundle/ruby/3.2.0/gems/benchmark-0.2.0/lib/benchmark.rb:311:in `realtime'
      # /bundle/ruby/3.2.0/gems/searchkick-5.3.0/lib/searchkick/controller_runtime.rb:20:in `cleanup_view_runtime'
      # /bundle/ruby/3.2.0/gems/mongoid-8.1.3/lib/mongoid/railties/controller_runtime.rb:27:in `cleanup_view_runtime'
      # /bundle/ruby/3.2.0/gems/wicked_pdf-2.6.3/lib/wicked_pdf/pdf_helper.rb:18:in `render'
      # /bundle/ruby/3.2.0/gems/actiontext-7.1.1/lib/action_text/rendering.rb:23:in `with_renderer'
      # /bundle/ruby/3.2.0/gems/actiontext-7.1.1/lib/action_text/engine.rb:69:in `block (4 levels) in <class:Engine>'
      # /bundle/ruby/3.2.0/gems/marginalia-1.11.1/lib/marginalia.rb:109:in `record_query_comment'
      # /bundle/ruby/3.2.0/gems/searchkick-5.3.0/lib/searchkick/controller_runtime.rb:15:in `process_action'
      # /bundle/ruby/3.2.0/gems/mongoid-8.1.3/lib/mongoid/railties/controller_runtime.rb:21:in `process_action'
      # /bundle/ruby/3.2.0/gems/rails-controller-testing-1.0.5/lib/rails/controller/testing/template_assertions.rb:62:in `process'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:35:in `block in process'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:104:in `catch'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:104:in `_catch_warden'
      # /bundle/ruby/3.2.0/gems/devise-4.9.0/lib/devise/test/controller_helpers.rb:35:in `process'
      # /bundle/ruby/3.2.0/gems/gon-6.4.0/lib/gon/spec_helpers.rb:15:in `process'
      # /bundle/ruby/3.2.0/gems/rails-controller-testing-1.0.5/lib/rails/controller/testing/integration.rb:16:in `block (2 levels) in <module:Integration>'
      # ./spec/controllers/showcase_widget/api/showcase_projects_controller_spec.rb:186:in `perform_get_request'
      # ./spec/controllers/showcase_widget/api/showcase_projects_controller_spec.rb:27:in `block (5 levels) in <top (required)>'
      # ./spec/spec_helper.rb:92:in `block (3 levels) in <top (required)>'
      # ./spec/spec_helper.rb:91:in `block (2 levels) in <top (required)>'
      # /bundle/ruby/3.2.0/gems/webmock-3.19.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
      # ------------------
      # --- Caused by: ---
      # TypeError:
      #   singleton can't be dumped
      #   ./app/views/showcase_widget/api/showcase_projects/index.json.jb:3:in _app_views_showcase_widget_api_showcase_projects_index_json_jb__984211662139926002_220520'

My Views

# index.json.jb
Rails.cache.fetch ["showcase-projects", @showcase, @showcase_params_cache_key] do
  json = {
    "totalCount": @total_count,
    "filteredCount": @filtered_count
  }

  json[:showcaseprojects] = render(partial: 'showcase_widget/api/showcase_projects/showcase_project', collection: @showcase_projects, as: :showcase_project)

  json
end
# showcase_project.json.jb

Rails.cache.fetch ["showcase-project", showcase_project] do
  json = {
    id: showcase_project.id.to_s,
    title: showcase_project.title,
    description: showcase_project.description,
    city: showcase_project.project.city,
    state: showcase_project.project.state,
    zipcode: showcase_project.project.postal_code,
    'lastUpdated': showcase_project.updated_at,
    'createdAt': showcase_project.created_at,
    'featuredPhoto': showcase_project.featured_image.url_medium,
    'materials': showcase_project.materials_used,
    'types': showcase_project.project_types,
    'mediaCount': showcase_project.showcase_project_media_count
  }

  json[:media] = render(
    partial: 'showcase_widget/api/showcase_projects/media',
    collection: showcase_project.showcase_project_media.visible.sort_by_captured_at.limit(4),
    as: :showcase_project_media
  )

  json
end
# media.json.jb
Rails.cache.fetch ["showcase-project-media", showcase_project_media] do
  {
    id: showcase_project_media.id.to_s,
    url: showcase_project_media.media.url_large,
  }
end

Updates

I attempted to inline rendering the entire object (removed calls to render(partial: …) and it works. Something about dumping the partial seems to be what is messing it up.

This works

Rails.cache.fetch ["showcase-projects", @showcase, @showcase_params_cache_key] do
  json = {
    "totalCount": @total_count,
    "filteredCount": @filtered_count
  }

  json[:showcaseprojects] = @showcase_projects.map do |showcase_project|
    project_json = {
      id: showcase_project.id.to_s,
      title: showcase_project.title,
      description: showcase_project.description,
      city: showcase_project.project.city,
      state: showcase_project.project.state,
      zipcode: showcase_project.project.postal_code,
      'lastUpdated': showcase_project.updated_at,
      'createdAt': showcase_project.created_at,
      'featuredPhoto': showcase_project.featured_image.url_medium,
      'materials': showcase_project.materials_used,
      'types': showcase_project.project_types,
      'mediaCount': showcase_project.showcase_project_media_count
    }

    project_json[:media] = showcase_project.showcase_project_media.visible.sort_by_captured_at.limit(4).map do |media|
      {
        id: media.id.to_s,
        url: media.media.url_large,
      }
    end

    project_json
  end

  json
end
amatsuda commented 1 year ago

@chadwilken Thank you for letting me know this. Your bug report was really helpful! I just fixed this bug on af5997e808c11ed06e56f393c2f562009e2ed57b and published jb 0.8.2 with the fix.

Thanks!

chadwilken commented 1 year ago

Thank you so much!