17

I just added a Postgres json type to a Rails/Active Record table I'm working with. I'd like to populate a record with a default value in Rails fixtures:

fixture_id:
  existing_column: "foobar"
  newly_added_column: <%= JSON.dump({:reason => 'foobar'}) %>

Previously, I'd stored stringified JSON this way in a text column. However, when I now run this unit test:

test "my test" do
  sut = fixtures(:fixture_id)
  assert_not_nil sut.newly_added_column
end

The test fails. Because it is JSON at the database level, I don't think it's useful to dump it to a string, but the YAML fixtures can't seem to keep an object as a Hash (when I try it without the JSON.dump, I get ActiveRecord::Fixture::FormatError: a YAML error occurred parsing).

Mind you, I am using Rails 3, so I think some of the support for this may be in Rails 4, but in Rails 3, the migration to add a json Postgres column type still work.

6 Answers 6

16

I believe your fixture can be as simple as:

fixture_id:
  existing_column: foobar
  newly_added_column: {'reason':'foobar'}
Sign up to request clarification or add additional context in comments.

Comments

13

To avoid having lengthy JSON structure inline in your fixtures, YAML provide a useful > operator

fixture_id:
  existing_column: "foobar"
  data: >
    {
      "can_edit": true,
      "can_se": true,
      "can_share": true,
      "something_else": true
    }

3 Comments

Had an issue adding a serialized JSON attribute in a fixture and this solved it for me. Thanks!
Worked perfectly for a JSON serialized attribute.
This did not work for me on Rails 6.1. It does not raise an error, but the content is not interpreted as a JSON but a String. In the example above, obj.data.is_a? String returns true. Without ">" operator, it is a Hash.
8

In Rails 5 it is possible to simply describe the JSON in YAML format. It will then be converted into correct JSON when fixtures are loaded in the DB:

parent:
  name: A fixture
  json_field:
    - name: JSON Object 1
      foo: bar
    - name: JSON Object 2
      foo: baz

(tested with JSONB attributes in Postgres)

1 Comment

This was the only method mentioned here that worked for me. Everything else was treated as a string (fresh rails 7 app). After i realized that I could output yaml at the console for some pretty complex JSON data it was ok. Just puts model.jsonb_attribute.to_yaml and copy paste that into your fixture.
2

If you are using ActiveRecord to generate content for your fixtures, you have to ensure that it converts attribute values to json. If you rely on the default serialization, it will produce hash rocket text which is invalid YAML.

For example, I have a legacy database that I am using to create fixtures for my new application (relevant code listed below):

presenter.rb

module FixturePresenter
  def replacer(str)
    str.downcase.tr(' ', '_').tr('-', '_')
  end

  # rubocop:disable Metrics/AbcSize
  def plan
    tag = replacer(name) + '_' + replacer(type)

    <<~FIXTURE
      #{tag}:
        name: #{name}
        type: #{type}
        address: #{address}
    FIXTURE
  end
end

runner.rb

...
Legacy::Provider.all.each_with_index do |p, i|
  p.extend(FixturePresenter)
  f.puts p.plan
end
...

The address column in the plan table is a JSONB datatype. When we run this code this is the resulting YAML:

abc_plan_la_jolla:
  name: ABC Plan La Jolla
  type: default
  address: {"street"=>"9888 Genesee Ave","unit"=>"","city"=>"La Jolla","state"=>"CA","postal_code"=>"92037"}

When you run your test you will get the very sad Syck error:

Psych::SyntaxError: (<unknown>): did not find expected ',' or '}' while parsing a flow mapping at line 4 column 12

The following change to the code will produce the correct YAML:

presenter.rb

address: #{address} to address: #{address.to_json}

The above change will produce a happy fixture definition:

abc_plan_la_jolla:
  name: ABC Plan La Jolla
  type: default
  address: {"street":"9888 Genesee Ave","unit":"","city":"La Jolla","state":"CA","postal_code":"92037"}

Comments

0

Try

fixture_id:
  existing_column: "foobar"
  newly_added_column: "{\"reason\": \"foobar\"}"

Comments

0

I was maintaining a legacy Rails 4.2 application and needed to put JSON store value into a fixture. I made the following monkey patch to fix the problem. Hope this helps someone:

module ActiveRecord
  class Fixture
    def to_hash
      h = fixture
      model_class.attribute_names.each do |name|
        typedef = model_class.type_for_attribute(name)
        h[name] = typedef.coder.dump(h[name]) \
          if typedef.is_a? ActiveRecord::Type::Serialized
      end
      h
    end
  end
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.