Skip to content

Instantly share code, notes, and snippets.

@haslo
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save haslo/c1cb3e549c2a3a8ee576 to your computer and use it in GitHub Desktop.
Save haslo/c1cb3e549c2a3a8ee576 to your computer and use it in GitHub Desktop.
Getters and Setters for JSON Data Fields for ActiveRecord Models
module Concerns
module ModelWithJSONData
extend ActiveSupport::Concern
module ClassMethods
def json_data_field_descriptions
[]
end
end
def method_missing(method, *args, &block)
self.class.json_data_field_descriptions.each do |field_description|
if field_description[:name].to_s == method.to_s
self.class.__send__(:define_method, field_description[:name].to_sym) do
self.json_data ||= {}
self.json_data[field_description[:name].to_s]
end
return __send__(field_description[:name])
end
if "#{field_description[:name]}=" == method.to_s
self.class.__send__(:define_method, "#{field_description[:name]}=".to_sym) do |value|
self.json_data ||= {}
self.json_data[field_description[:name].to_s] = __send__(field_description[:data_type].name, value)
end
return __send__("#{field_description[:name]}=", args[0])
end
end
super
end
end
end
@haslo
Copy link
Author

haslo commented Oct 23, 2014

Here's the spec:

# shared examples file in /spec/support

shared_examples 'uses JSON data' do

  it 'does not raise an error for field getter methods' do
    subject.class.json_data_fields.each do |field|
      expect{(subject.send(field))}.not_to raise_error
    end
  end

  it 'does not raise an error for field setter methods' do
    subject.class.json_data_fields.each do |field|
      expect{(subject.send("#{field}=", 'test'))}.not_to raise_error
    end
  end

  context 'uninitialized json data fields' do
    it 'responds with nil for each field' do
      subject.class.json_data_fields.each do |field|
        expect(subject.send(field)).to be nil
      end
    end
  end

  context 'setters and getters' do
    it 'responds with the stored value for each field after using the setter' do
      subject.class.json_data_fields.each do |field|
        expect(subject.send("#{field}=", 'test')).to eq('test')
        expect(subject.send(field)).to eq('test')
      end
    end
    it 'responds with the stored value for each field after using the setter twice' do
      subject.class.json_data_fields.each do |field|
        expect(subject.send("#{field}=", 'test1')).to eq('test1')
        expect(subject.send(field)).to eq('test1')
        expect(subject.send("#{field}=", 'test2')).to eq('test2')
        expect(subject.send(field)).to eq('test2')
      end
    end
  end

  context 'JSON storage' do
    it 'stores the value in the json_data field when using the setter' do
      subject.class.json_data_fields.each do |field|
        expect(subject.send("#{field}=", 'test1')).to eq('test1')
        expect(subject.send("#{field}=", 'test2')).to eq('test2')
        expect(subject.json_data[field.to_s]).to eq('test2')
      end
    end
    it 'retrieves the value from the json_data field when using the getter' do
      subject.class.json_data_fields.each do |field|
        expect(subject.send("#{field}=", 'test1')).to eq('test1')
        subject.json_data[field.to_s] = 'test2'
        expect(subject.send(field.to_s)).to eq('test2')
      end
    end
  end

end

# spec with double

module TestTemps
  class ModelWithJSONDataDouble
    include Concerns::ModelWithJSONData
    def self.json_data_fields
      [
        :first_field,
        :second_field
      ]
    end
    def json_data
      @json_data ||= {}
    end
  end
end

describe Concerns::ModelWithJSONData do

  subject { TestTemps::ModelWithJSONDataDouble.new }

  it_behaves_like 'uses JSON data'

end

@kreba
Copy link

kreba commented Oct 24, 2014

Hmm, macht das etwas, das mit

class Foo < ActiveRecord::Base
  serialize :first_field, JSON
  serialize :second_field, JSON
end

nicht abgedeckt wäre? Oder sehe ich hier etwas nicht?

@haslo
Copy link
Author

haslo commented Oct 26, 2014

Haben das ja schon besprochen, es macht was ganz anderes 😁 Habe das Gist jetzt noch aktualisiert, die JSON-abstrahierten Datenfelder sind jetzt typisiert und validierbar.

https://github.com/haslo/tournament_tool/blob/master/spec/support/model_with_json_examples.rb

      context 'data types' do
        it 'does allow assignment of Strings to String fields' do
          subject.class.json_data_field_descriptions.each do |field_description|
            if field_description[:data_type] == String
              expect(subject.send("#{field_description[:name]}=", 'string')).to eq('string')
              expect(subject.send(field_description[:name])).to eq('string')
            end
          end
        end
        it 'does not allow assignment of non-Integers to Integer fields' do
          subject.class.json_data_field_descriptions.each do |field_description|
            if field_description[:data_type] == Integer
              expect{subject.send("#{field_description[:name]}=", {invalid: 'string'})}.to raise_error(TypeError)
            end
          end
        end
        it 'does not allow assignment of non-Hashes to Hash fields' do
          subject.class.json_data_field_descriptions.each do |field_description|
            if field_description[:data_type] == Hash
              expect{subject.send("#{field_description[:name]}=", 'not a hash')}.to raise_error(TypeError)
            end
          end
        end
        it 'does allow assignment of Strings to Array fields with implicit conversion' do
          subject.class.json_data_field_descriptions.each do |field_description|
            if field_description[:data_type] == Array
              expect(subject.send("#{field_description[:name]}=", 'string')).to eq(['string'])
              expect(subject.send(field_description[:name])).to eq(['string'])
            end
          end
        end
      end

https://github.com/haslo/tournament_tool/blob/master/spec/models/concerns/model_with_json_data_spec.rb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment