require 'spec_helper' require 'stringio' if defined? ActiveRecord # Prepare for tests ActiveRecord ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:' # Database migrations ActiveRecord::Migration.create_table :test_users do |t| t.string :name t.date :birthday t.timestamp :timestamp end ActiveRecord::Migration.create_table :test_orders do |t| t.references :user t.string :item t.integer :quantity t.date :purchased_on t.timestamp :timestamp end # TestUser model class TestUser < ActiveRecord::Base attr_accessible :id, :name, :birthday, :timestamp has_many :orders end # TestOrder model class TestOrder < ActiveRecord::Base attr_accessible :id, :user_id, :item, :quantity, :purchased_on, :timestamp belongs_to :user end # TestOrder model with configured table name and attributes class ConfiguredTestOrder < TestOrder flydata_table :other_table flydata_attr :id, :item, :purchased_on, :timestamp end end if defined? ActiveModel # TestUserModel model class TestUserModel include ActiveModel::Serialization attr_accessor :name, :birthday, :timestamp def initialize(attributes = {}) attributes.each do |name, value| send("#{name}=", value) end end def attributes { 'name' => name, 'birthday' => birthday, 'timestamp' => timestamp, } end end # TestOrderModel model class TestOrderModel include ActiveModel::Serialization attr_accessor :user_id, :item, :quantity, :purchased_on, :timestamp def initialize(attributes = {}) attributes.each do |name, value| send("#{name}=", value) end end def attributes { 'user_id' => user_id, 'item' => item, 'quantity' => quantity, 'purchased_on' => purchased_on, 'timestamp' => timestamp, } end end # TestOrder model with configured table name and attributes class ConfiguredTestOrderModel < TestOrderModel flydata_table :other_table flydata_attr :item, :quantity end end # Captures and returns output to specified stream. def capture(stream) begin stream = stream.to_s eval "$#{stream} = StringIO.new" yield result = eval("$#{stream}").string ensure eval "$#{stream} = #{stream.upcase}" end result end describe Flydata do describe '.send_to' do before do # Use ActiveSupport::TimeWithZone Time.zone = 'Japan' end let(:item_hash) do { id: 1, name: 'orange', date: Date.new(2013, 6, 1), time: Time.local(2013, 6, 1, 11, 22, 33), date_time: Time.local(2013, 6, 1, 11, 22, 33).to_datetime, time_with_zone: Time.zone.local(2013, 6, 1, 11, 22, 33), } end let(:item_hash_json) do json = <<-EOS { "items": { "id": 1, "name": "orange", "date": "2013-06-01", "time": "2013-06-01 11:22:33", "date_time": "2013-06-01 11:22:33", "time_with_zone": "2013-06-01 11:22:33" } } EOS JSON.parse(json).to_json end let(:item_array) do [ { id: 1, name: 'orange', date: Date.new(2013, 6, 1), time: Time.local(2013, 6, 1, 11, 22, 33), date_time: Time.local(2013, 6, 1, 11, 22, 33).to_datetime, time_with_zone: Time.zone.local(2013, 6, 1, 11, 22, 33), }, { id: 2, name: 'banana', date: Date.new(2013, 6, 2), time: Time.local(2013, 6, 2, 11, 22, 33), date_time: Time.local(2013, 6, 2, 11, 22, 33).to_datetime, time_with_zone: Time.zone.local(2013, 6, 2, 11, 22, 33), }, ] end let(:item_array_json) do json = <<-EOS { "items": [{ "id": 1, "name": "orange", "date": "2013-06-01", "time": "2013-06-01 11:22:33", "date_time": "2013-06-01 11:22:33", "time_with_zone": "2013-06-01 11:22:33" }, { "id": 2, "name": "banana", "date": "2013-06-02", "time": "2013-06-02 11:22:33", "date_time": "2013-06-02 11:22:33", "time_with_zone": "2013-06-02 11:22:33" } ] } EOS JSON.parse(json).to_json end context '1st argument is a nil' do it 'raises an ArgumentError' do expect { capture(:stdout) { described_class.send_to } }.to raise_error(ArgumentError, 'The argument of send_to must be non-nil') end end context '1st argument is a String' do context '1st argument is an empty String' do it 'raises an ArgumentError' do expect { capture(:stdout) { described_class.send_to('') } }.to raise_error(ArgumentError, 'The 1st argument of send_to must not be empty') end end context '2nd argument is a Hash' do it 'puts a JSON string' do actual = capture(:stdout) do described_class.send_to('items', item_hash) end.chomp expect(actual).to eq(item_hash_json) end end context '2nd argument is an Array' do it 'puts a JSON string' do actual = capture(:stdout) do described_class.send_to('items', item_array) end.chomp expect(actual).to eq(item_array_json) end end context '2nd argument is nil' do it 'raises an ArgumentError' do expect { capture(:stdout) { described_class.send_to('items') } }.to raise_error(ArgumentError, 'The 2nd argument of send_to must be an Array or Hash') end end context '2nd argument is a String' do it 'raises an ArgumentError' do expect { capture(:stdout) { described_class.send_to('items', 'foo') } }.to raise_error(ArgumentError) end end end context '1st argument is kind of a Symbol' do context '1st argument is an empty Symbol' do it 'raises an ArgumentError' do expect { capture(:stdout) { described_class.send_to(:'') } }.to raise_error(ArgumentError, 'The 1st argument of send_to must not be empty') end end context '2nd argument is a Hash' do it 'puts a JSON string' do actual = capture(:stdout) do described_class.send_to(:items, item_hash) end.chomp expect(actual).to eq(item_hash_json) end end context '2nd argument is an Array' do it 'puts a JSON string' do actual = capture(:stdout) do described_class.send_to(:items, item_array) end.chomp expect(actual).to eq(item_array_json) end end end context '1st argument is an empty Array' do it 'raises an ArgumentError' do expect { capture(:stdout) { described_class.send_to([]) } }.to raise_error(ArgumentError, 'The 1st argument of send_to must not be empty') end end context '1st argument is an empty Hash' do it 'raises an ArgumentError' do expect { capture(:stdout) { described_class.send_to({}) } }.to raise_error(ArgumentError, 'The 1st argument of send_to must not be empty') end end describe 'ActiveRecord support' do let!(:user) do TestUser.create!(name: 'John Doe', birthday: Date.new(2010, 1, 2), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) end let!(:order1) do TestOrder.create!(user_id: user.id, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) end let!(:order2) do TestOrder.create!(user_id: user.id, item: 'banana', quantity: 12, purchased_on: Date.new(2013, 6, 2), timestamp: Time.local(2013, 6, 2, 11, 22, 33)) end context '1st argument is an ActiveRecord::Base' do let(:first_argument) { order1 } it 'puts a JSON string' do actual = capture(:stdout) { described_class.send_to(first_argument) }.chomp json = <<-EOS { "test_orders": { "id": #{order1.id}, "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": #{user.id} } } EOS expectation = JSON.parse(json).to_json expect(actual).to eq(expectation) end end context '1st argument is an Array of ActiveRecord::Base' do let(:first_argument) { [order1, order2] } it 'puts a JSON string' do actual = capture(:stdout) { described_class.send_to(first_argument) }.chomp json = <<-EOS { "test_orders": [{ "id": #{order1.id}, "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": #{user.id} }, { "id": #{order2.id}, "item": "banana", "purchased_on": "2013-06-02", "quantity": 12, "timestamp": "2013-06-02 11:22:33", "user_id": #{user.id} } ] } EOS expectation = JSON.parse(json).to_json expect(actual).to eq(expectation) end end context 'Arguments are ActiveRecord::Base' do let(:first_argument) { user } let(:second_argument) { order1 } let(:third_argument) { order2 } it 'puts a JSON string represents all arguments' do actual = capture(:stdout) do described_class.send_to(first_argument, second_argument, third_argument) end.chomp json = <<-EOS { "test_orders": [{ "id": #{order1.id}, "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": #{user.id} }, { "id": #{order2.id}, "item": "banana", "purchased_on": "2013-06-02", "quantity": 12, "timestamp": "2013-06-02 11:22:33", "user_id": #{user.id} } ], "test_users": { "birthday": "2010-01-02", "id": #{user.id}, "name": "John Doe", "timestamp": "2013-06-02 11:22:33" } } EOS expectation = JSON.parse(json).to_json end end end describe 'ActiveModel support' do context '1st argument is includes ActiveModel::Serialization' do let(:first_argument) do TestOrderModel.new(user_id: 2, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) end it 'puts a JSON string represents 1st argument' do actual = capture(:stdout) { described_class.send_to(first_argument) }.chomp json = <<-EOS { "test_order_models": { "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": 2 } } EOS expectation = JSON.parse(json).to_json expect(actual).to eq(expectation) end end context '1st argument is an Array of models which includes ActiveModel::Serialization' do let(:first_argument) do [ TestOrderModel.new(user_id: 2, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)), TestOrderModel.new(user_id: 2, item: 'banana', quantity: 12, purchased_on: Date.new(2013, 7, 1), timestamp: Time.local(2013, 7, 1, 11, 22, 33)), ] end it 'puts a JSON string represents 1st argument' do actual = capture(:stdout) { described_class.send_to(first_argument) }.chomp json = <<-EOS { "test_order_models": [{ "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": 2 }, { "item": "banana", "purchased_on": "2013-07-01", "quantity": 12, "timestamp": "2013-07-01 11:22:33", "user_id": 2 } ] } EOS expectation = JSON.parse(json).to_json expect(actual).to eq(expectation) end end context 'Arguments are including ActiveModel::Base' do let(:first_argument) { TestOrderModel.new(user_id: 2, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) } let(:second_argument) { TestOrderModel.new(user_id: 2, item: 'banana', quantity: 12, purchased_on: Date.new(2013, 7, 1), timestamp: Time.local(2013, 7, 1, 11, 22, 33)) } let(:third_argument) { TestUserModel.new(name: 'John') } it 'puts a JSON string represents all arguments' do actual = capture(:stdout) do described_class.send_to(first_argument, second_argument, third_argument) end.chomp json = <<-EOS { "test_order_models": [{ "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": 2 }, { "item": "banana", "purchased_on": "2013-07-01", "quantity": 12, "timestamp": "2013-07-01 11:22:33", "user_id": 2 } ], "test_user_models": { "birthday": null, "name": "John", "timestamp": null } } EOS expectation = JSON.parse(json).to_json expect(actual).to eq(expectation) end end end end if defined? ActiveRecord describe 'ActiveRecord::Base#send_to_flydata' do let(:order) do TestOrder.create!(id: 1, user_id: 2, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) end it 'puts a JSON string represents a model' do actual = capture(:stdout) { order.send_to_flydata }.chomp json = <<-EOS { "test_orders": { "id": 1, "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": 2 } } EOS expectation = JSON.parse(json).to_json expect(actual).to eq(expectation) end end describe 'ActiveRecord::Base.flydata_table' do let(:order) do ConfiguredTestOrder.create!(id: 1, user_id: 2, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) end it 'puts a JSON string represents a model to configured table name' do output = capture(:stdout) { order.send_to_flydata } actual = JSON.parse(output) expect(actual.keys).to include('other_table') expect(actual.keys).not_to include('test_orders') end end describe 'ActiveRecord::Base.flydata_attr' do let(:order) do ConfiguredTestOrder.create!(id: 1, user_id: 2, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) end it 'puts a JSON string represents a model to configured attributes' do actual = capture(:stdout) { order.send_to_flydata }.chomp json = <<-EOS { "other_table": { "id": 1, "item": "orange", "purchased_on": "2013-06-01", "timestamp": "2013-06-01 11:22:33" } } EOS expectation = JSON.parse(json).to_json expect(actual).to eq(expectation) end context 'without attributes' do it 'raise exception' do expect { class InvalidConfiguredTestOrder < TestOrder flydata_attr end }.to raise_error(RuntimeError, 'flydata_attr attributes cannot be blank') end end end end if defined? ActiveModel describe 'ActiveModel::Serialization#send_to_flydata' do let(:order) do TestOrderModel.new(user_id: 2, item: 'orange', quantity: 6, purchased_on: Date.new(2013, 6, 1), timestamp: Time.local(2013, 6, 1, 11, 22, 33)) end it 'puts a JSON string represents a model' do actual = capture(:stdout) { order.send_to_flydata }.chomp json = <<-EOS { "test_orders": { "item": "orange", "purchased_on": "2013-06-01", "quantity": 6, "timestamp": "2013-06-01 11:22:33", "user_id": 2 } } EOS expectation = JSON.parse(json).to_json end end describe 'ActiveModel::Serialization.flydata_table' do let(:order) { ConfiguredTestOrderModel.new(user_id: 2, item: 'orange', quantity: 6) } it 'puts a JSON string represents a model to configured table name' do output = capture(:stdout) { order.send_to_flydata } actual = JSON.parse(output) expect(actual.keys).to include('other_table') expect(actual.keys).not_to include('test_orders') end end describe 'ActiveModel::Serialization.flydata_attr' do let(:order) { ConfiguredTestOrderModel.new(user_id: 2, item: 'orange', quantity: 6) } it 'puts a JSON string represents a model to configured attributes' do output = capture(:stdout) { order.send_to_flydata } actual = JSON.parse(output) expect(actual['other_table'].keys).to include('item') expect(actual['other_table'].keys).to include('quantity') expect(actual['other_table'].keys).not_to include('purchased_on') expect(actual['other_table'].keys).not_to include('timestamp') expect(actual['other_table'].keys).not_to include('user_id') end context 'without attributes' do it 'raise exception' do expect { class InvalidConfiguredTestOrderModel < TestOrderModel flydata_attr end }.to raise_error(RuntimeError, 'flydata_attr attributes cannot be blank') end end end end end