From 29d977a0e7494a6c33061d2dc7a750ae2c6bf88e Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 19 Feb 2017 18:46:43 -0800 Subject: [PATCH 01/18] Update `rom` dependency to `~> 3.0` --- rom-csv.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rom-csv.gemspec b/rom-csv.gemspec index 626ede2..e698502 100644 --- a/rom-csv.gemspec +++ b/rom-csv.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_runtime_dependency 'rom', '~> 1.0' + spec.add_runtime_dependency 'rom', '~> 3.0' spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake' From 0fee3b9ac57c1965ae6b60d745d04852bfc545ea Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 19 Feb 2017 18:50:12 -0800 Subject: [PATCH 02/18] Remove deprecated `use(:macros)` call --- examples/find_user.rb | 1 - spec/support/database_setup.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/find_user.rb b/examples/find_user.rb index 5bb460f..22542cf 100644 --- a/examples/find_user.rb +++ b/examples/find_user.rb @@ -7,7 +7,6 @@ csv_file = ARGV[0] || File.expand_path("./users.csv", File.dirname(__FILE__)) configuration = ROM::Configuration.new(:csv, csv_file) -configuration.use(:macros) configuration.relation(:users) do def by_name(name) diff --git a/spec/support/database_setup.rb b/spec/support/database_setup.rb index c57a420..539f6af 100644 --- a/spec/support/database_setup.rb +++ b/spec/support/database_setup.rb @@ -5,7 +5,7 @@ users: [:csv, path], addresses: [:csv, addresses_path], utf8: [:csv, users_with_utf8_path, { encoding: 'iso-8859-2', col_sep: ';' }] - ).use(:macros) + ) end let(:container) { ROM.container(configuration) } From 6521d6483c2cf37bdbf76acbbd6832934a4cc128 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 19 Feb 2017 19:05:44 -0800 Subject: [PATCH 03/18] Remove deprecated `validator` from commands This was removed from `ROM::Command` as part of the move to 3.x. Data should be validated before being sent to commands. See: rom commit 80bb84 --- lib/rom/csv/commands/create.rb | 1 - lib/rom/csv/commands/update.rb | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/rom/csv/commands/create.rb b/lib/rom/csv/commands/create.rb index adfd2a4..e92030a 100644 --- a/lib/rom/csv/commands/create.rb +++ b/lib/rom/csv/commands/create.rb @@ -10,7 +10,6 @@ class Create < ROM::Commands::Create def execute(tuples) insert_tuples = [tuples].flatten.map do |tuple| attributes = input[tuple] - validator.call(attributes) attributes.to_h end diff --git a/lib/rom/csv/commands/update.rb b/lib/rom/csv/commands/update.rb index 5add615..e1144c0 100644 --- a/lib/rom/csv/commands/update.rb +++ b/lib/rom/csv/commands/update.rb @@ -9,7 +9,6 @@ class Update < ROM::Commands::Update def execute(tuple) attributes = input[tuple] - validator.call(attributes) tuple = attributes.to_h update(tuple) From 662eb12b3a99b9651995ac1eac315eace056d601 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 19 Feb 2017 23:40:04 -0800 Subject: [PATCH 04/18] Remove tuple count assertion examples The `assert_tuple_count` checking was removed from `ROM::Command` during the 2.x work. The underlying command no longer performs this check so the spec was failing. See: rom commit bec2c4 --- spec/integration/commands/delete_spec.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/spec/integration/commands/delete_spec.rb b/spec/integration/commands/delete_spec.rb index fa32eb9..fe992fe 100644 --- a/spec/integration/commands/delete_spec.rb +++ b/spec/integration/commands/delete_spec.rb @@ -19,13 +19,6 @@ def by_id(id) end end - it 'raises error when tuple count does not match expectation' do - result = users.try { users.delete.call } - - expect(result.value).to be(nil) - expect(result.error).to be_instance_of(ROM::TupleCountMismatchError) - end - it 'deletes all tuples in a restricted relation' do result = users.try { users.delete.by_id(1).call } From 5ff17dfdf596d6c86ae5ab748e102250dfe49407 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 19 Feb 2017 23:42:48 -0800 Subject: [PATCH 05/18] Update `dataset` to support dry-initializer `ROM::Memory::Dataset` was updated to use `dry-initializer` so the calls to `option` in dataset here are now updated using the new syntax with some bonus type checking courtesy of `dry-types`. See: rom commit 25f937 --- lib/rom/csv/dataset.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/rom/csv/dataset.rb b/lib/rom/csv/dataset.rb index 293bfce..97e88b4 100644 --- a/lib/rom/csv/dataset.rb +++ b/lib/rom/csv/dataset.rb @@ -6,8 +6,12 @@ module CSV # # @api public class Dataset < ROM::Memory::Dataset - option :path, reader: true - option :file_options, reader: true + option :path, + optional: true, + type: Dry::Types['strict.string'] + option :file_options, + default: proc { {} }, + type: Dry::Types['strict.hash'] # Convert each CSV::Row to a hash # From a99ea9dfd048c14d72ad59db433acd50e906a531 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Mon, 20 Feb 2017 00:09:37 -0800 Subject: [PATCH 06/18] Update Travis rvm configuration to match rom gems Support the same ruby versions as other `rom-*` gems. --- .travis.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7fd1b23..d004c5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,22 @@ language: ruby +dist: trusty +cache: bundler bundler_args: --without tools script: "bundle exec rake ci" env: - CODECLIMATE_REPO_TOKEN=a49f28520e49a35d73c9b6ed3d78ae110e4a5d05b9f154b999578a5175add09d + global: + - JRUBY_OPTS='--dev -J-Xmx1024M' rvm: - - 2.0 - - 2.1 + - 2.4.0 + - 2.3 - 2.2 - - 2.3.0 - - rbx-2 - - jruby-9000 + - rbx-3 + - jruby-9.1.7.0 - ruby-head matrix: allow_failures: + - rvm: rbx-3 - rvm: ruby-head notifications: webhooks: From cae7a8eb8f791be50173d7b5198b261384655009 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Mon, 20 Feb 2017 00:23:41 -0800 Subject: [PATCH 07/18] Update Travis code coverage configuration --- .travis.yml | 4 +++- spec/spec_helper.rb | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d004c5f..37c1c89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,11 @@ cache: bundler bundler_args: --without tools script: "bundle exec rake ci" env: - - CODECLIMATE_REPO_TOKEN=a49f28520e49a35d73c9b6ed3d78ae110e4a5d05b9f154b999578a5175add09d global: + - CODECLIMATE_REPO_TOKEN=a49f28520e49a35d73c9b6ed3d78ae110e4a5d05b9f154b999578a5175add09d - JRUBY_OPTS='--dev -J-Xmx1024M' +after_success: + - - '[ "${TRAVIS_JOB_NUMBER#*.}" = "1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter' rvm: - 2.4.0 - 2.3 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 113eddd..39d827a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,9 +3,11 @@ require 'bundler' Bundler.require -if RUBY_ENGINE == 'rbx' - require 'codeclimate-test-reporter' - CodeClimate::TestReporter.start +if RUBY_ENGINE == 'ruby' && ENV['CI'] == 'true' + require 'simplecov' + SimpleCov.start do + add_filter '/spec/' + end end require 'rom' From ed58c21ddd3c0edb89174da68535fca76c1350ed Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sat, 25 Feb 2017 14:30:20 -0800 Subject: [PATCH 08/18] Update development dependencies in gemspec --- rom-csv.gemspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rom-csv.gemspec b/rom-csv.gemspec index e698502..edb33dd 100644 --- a/rom-csv.gemspec +++ b/rom-csv.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'rom', '~> 3.0' spec.add_development_dependency 'bundler' - spec.add_development_dependency 'rake' + spec.add_development_dependency 'rake', '10.3' + spec.add_development_dependency 'rspec', '~> 3.5' spec.add_development_dependency 'rubocop', '~> 0.28.0' end From c4c3b37e3ba95309b300cfe07fd2e51a9866bdde Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sat, 25 Feb 2017 16:50:52 -0800 Subject: [PATCH 09/18] Update `spec_helper` to provide `Test` module Provides a `Test` module to the specs to isolate test-specific classes and objects. This is similar to setup in other rom-* gems. --- spec/spec_helper.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 39d827a..328dff0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,6 +13,17 @@ require 'rom' require 'rom-csv' -root = Pathname(__FILE__).dirname +SPEC_ROOT = root = Pathname(__FILE__).dirname -Dir[root.join('support/*.rb').to_s].each { |f| require f } +RSpec.configure do |config| + config.before do + module Test + end + end + + config.after do + Object.send(:remove_const, :Test) + end + + Dir[root.join('support/*.rb').to_s].each { |f| require f } +end From 52604286422dfc404db65c141a8a5d7315ef2add Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sat, 25 Feb 2017 16:52:01 -0800 Subject: [PATCH 10/18] Replace virtus in specs with dry-struct --- Gemfile | 2 +- spec/integration/commands/create_spec.rb | 16 ++++---- spec/integration/commands/update_spec.rb | 16 ++++---- spec/integration/relation_spec.rb | 1 - spec/integration/repository_spec.rb | 48 ++++++++++++------------ spec/spec_helper.rb | 6 +++ 6 files changed, 46 insertions(+), 43 deletions(-) diff --git a/Gemfile b/Gemfile index b12fcbf..2fd9843 100644 --- a/Gemfile +++ b/Gemfile @@ -3,8 +3,8 @@ source 'https://rubygems.org' gemspec group :test do + gem 'dry-struct' gem 'rom', git: 'https://github.com/rom-rb/rom.git', branch: 'master' - gem 'virtus' gem 'rspec', '~> 3.1' gem 'codeclimate-test-reporter', require: false gem 'inflecto' diff --git a/spec/integration/commands/create_spec.rb b/spec/integration/commands/create_spec.rb index 3f80c08..240ea40 100644 --- a/spec/integration/commands/create_spec.rb +++ b/spec/integration/commands/create_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require 'virtus' +require 'dry-struct' describe 'Commands / Create' do include_context 'database setup' @@ -9,17 +9,17 @@ before do configuration.relation(:users) - class User - include Virtus.model - - attribute :id, Integer - attribute :name, String - attribute :email, String + module Test + class User < Dry::Struct + attribute :user_id, Types::Strict::Int + attribute :name, Types::Strict::String + attribute :email, Types::Strict::String + end end configuration.mappers do define(:users) do - model User + model Test::User register_as :entity end end diff --git a/spec/integration/commands/update_spec.rb b/spec/integration/commands/update_spec.rb index 9687a29..68b6418 100644 --- a/spec/integration/commands/update_spec.rb +++ b/spec/integration/commands/update_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require 'virtus' +require 'dry-struct' describe 'Commands / Updates' do include_context 'database setup' @@ -20,17 +20,17 @@ def by_id(id) end end - class User - include Virtus.model - - attribute :id, Integer - attribute :name, String - attribute :email, String + module Test + class User < Dry::Struct + attribute :user_id, Types::Strict::Int + attribute :name, Types::Strict::String + attribute :email, Types::Strict::String + end end configuration.mappers do define(:users) do - model User + model Test::User register_as :entity end end diff --git a/spec/integration/relation_spec.rb b/spec/integration/relation_spec.rb index d2860fb..94b0f54 100644 --- a/spec/integration/relation_spec.rb +++ b/spec/integration/relation_spec.rb @@ -1,5 +1,4 @@ require 'spec_helper' -require 'virtus' describe 'CSV gateway' do context 'without extra options' do diff --git a/spec/integration/repository_spec.rb b/spec/integration/repository_spec.rb index def4472..e4a80ba 100644 --- a/spec/integration/repository_spec.rb +++ b/spec/integration/repository_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require 'virtus' +require 'dry-struct' describe 'CSV gateway' do context 'without extra options' do @@ -30,42 +30,40 @@ def with_addresses gateway :addresses end - class User - include Virtus.model + module Test + class User < Dry::Struct + constructor_type :schema # allow missing keys - attribute :user_id, Integer - attribute :name, String - attribute :email, String - end - - class UserWithAddress - include Virtus.model - - attribute :user_id, Integer - attribute :name, String - attribute :email, String - attribute :addresses - end + attribute :user_id, Types::Strict::Int.optional + attribute :name, Types::Strict::String + attribute :email, Types::Strict::String.optional + end - class Address - include Virtus.model + class Address < Dry::Struct + attribute :address_id, Types::Strict::Int + attribute :street, Types::Strict::String + end - attribute :address_id, Integer - attribute :street, String + class UserWithAddress < Dry::Struct + attribute :user_id, Types::Strict::Int + attribute :name, Types::Strict::String + attribute :email, Types::Strict::String + attribute :addresses, Types::Strict::Array.member(Test::Address) + end end configuration.mappers do define(:users) do - model User + model Test::User register_as :entity end define(:users_with_address, parent: :users) do - model UserWithAddress + model Test::UserWithAddress register_as :entity_with_address group :addresses do - model Address + model Test::Address attribute :address_id attribute :street @@ -107,10 +105,10 @@ class Address results = container.relation(:users).as(:entity_with_address) .with_addresses.first - expect(results.attributes.keys.sort) + expect(results.to_hash.keys.sort) .to eq([:user_id, :name, :email, :addresses].sort) - expect(results.addresses.first.attributes.keys.sort) + expect(results.addresses.first.to_hash.keys.sort) .to eq([:address_id, :street].sort) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 328dff0..657f631 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -15,6 +15,12 @@ SPEC_ROOT = root = Pathname(__FILE__).dirname +# Provide a `Types` module to specs +require 'dry-types' +module Types + include Dry::Types.module +end + RSpec.configure do |config| config.before do module Test From 3ffb3ef04dc91f16ff84a7e1e0e4cab1c0ed4a17 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sat, 25 Feb 2017 16:57:05 -0800 Subject: [PATCH 11/18] Complete `Repository` -> `Gateway` There were still a few references to repository which are now removed. --- README.md | 2 +- spec/integration/{repository_spec.rb => gateway_spec.rb} | 0 spec/unit/{repository_spec.rb => gateway_spec.rb} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename spec/integration/{repository_spec.rb => gateway_spec.rb} (100%) rename spec/unit/{repository_spec.rb => gateway_spec.rb} (100%) diff --git a/README.md b/README.md index bf7867d..b6615c5 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Or install it yourself as: ## Usage -See `spec/integration/repository_spec.rb` or [examples/](https://github.com/rom-rb/rom-csv/tree/master/examples) folder for a sample usage. +See `spec/integration/gateway_spec.rb` or [examples/](https://github.com/rom-rb/rom-csv/tree/master/examples) folder for a sample usage. ## License diff --git a/spec/integration/repository_spec.rb b/spec/integration/gateway_spec.rb similarity index 100% rename from spec/integration/repository_spec.rb rename to spec/integration/gateway_spec.rb diff --git a/spec/unit/repository_spec.rb b/spec/unit/gateway_spec.rb similarity index 100% rename from spec/unit/repository_spec.rb rename to spec/unit/gateway_spec.rb From 2643d657f269163826810ea1ac7a00dcae2ac8b4 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 26 Feb 2017 11:56:37 -0800 Subject: [PATCH 12/18] Use `ROM::Initializer` in `CSV::Gateway` Updates the gateway to use the ROM initializer class, which is a thin wrapper around `dry-initializer` - eliminating some boilerplate and allow for some type enforcement for params. Of note, the `options` param was renamed to `csv_options` since `dry-initializer` *did not like* a param named `options`. Also, the `datasets` attr reader was moved up to make it a little more obvious. --- lib/rom/csv/gateway.rb | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/rom/csv/gateway.rb b/lib/rom/csv/gateway.rb index 3875207..f2127b2 100644 --- a/lib/rom/csv/gateway.rb +++ b/lib/rom/csv/gateway.rb @@ -1,4 +1,5 @@ require 'rom/gateway' +require 'rom/initializer' require 'rom/csv/dataset' require 'rom/csv/commands' @@ -40,6 +41,20 @@ module CSV # # @api public class Gateway < ROM::Gateway + extend Initializer + + param :path, + reader: :private, + type: Dry::Types['strict.string'] + param :csv_options, + default: proc { {} }, + reader: :private, + type: Dry::Types['strict.hash'] + + # @api private + attr_reader :datasets + private :datasets + # Expect a path to a single csv file which will be registered by rom to # the given name or :default as the gateway. # @@ -49,16 +64,15 @@ class Gateway < ROM::Gateway # * header_converters: :symbol # # @param path [String] path to csv - # @param options [Hash] options passed to CSV.table + # @param csv_options [Hash] options passed to CSV.table # # @api private # # @see CSV.table - def initialize(path, options = {}) + def initialize(*) + super @datasets = {} - @path = path - @options = options - @connection = ::CSV.table(path, options).by_row! + @connection = ::CSV.table(path, csv_options).by_row! end # Return dataset with the given name @@ -95,11 +109,8 @@ def dataset?(name) private def dataset_options - { path: path, file_options: options } + { path: path, file_options: csv_options } end - - # @api private - attr_reader :datasets, :path, :options end end end From abb1651e3e91bd5ffd6f1c9c5006c3a3f212783d Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 26 Feb 2017 12:08:27 -0800 Subject: [PATCH 13/18] Use `Test` module in relation spec Updates the `relation_spec` to use the new `Test` module to isolate the `RelationPlugin` module created for the examples. --- spec/integration/relation_spec.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/spec/integration/relation_spec.rb b/spec/integration/relation_spec.rb index 94b0f54..2868c4e 100644 --- a/spec/integration/relation_spec.rb +++ b/spec/integration/relation_spec.rb @@ -1,15 +1,19 @@ require 'spec_helper' -describe 'CSV gateway' do +describe 'CSV relation' do context 'without extra options' do include_context 'database setup' before do - module TestPlugin; end + module Test + module RelationPlugin + # empty plugin for test purposes + end + end ROM.plugins do adapter :csv do - register :test_plugin, TestPlugin, type: :relation + register :test_plugin, Test::RelationPlugin, type: :relation end end end From d12024a135eb74b08360ee94b93f6017caf68273 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 26 Feb 2017 13:31:09 -0800 Subject: [PATCH 14/18] Add `Types` module and use in definitions Use the new `ROM::CSV::Types` module in the params/options definitions throughout. --- lib/rom/csv/dataset.rb | 4 ++-- lib/rom/csv/gateway.rb | 4 ++-- lib/rom/csv/types.rb | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 lib/rom/csv/types.rb diff --git a/lib/rom/csv/dataset.rb b/lib/rom/csv/dataset.rb index 97e88b4..838df77 100644 --- a/lib/rom/csv/dataset.rb +++ b/lib/rom/csv/dataset.rb @@ -8,10 +8,10 @@ module CSV class Dataset < ROM::Memory::Dataset option :path, optional: true, - type: Dry::Types['strict.string'] + type: Types::Strict::String option :file_options, default: proc { {} }, - type: Dry::Types['strict.hash'] + type: Types::Strict::Hash # Convert each CSV::Row to a hash # diff --git a/lib/rom/csv/gateway.rb b/lib/rom/csv/gateway.rb index f2127b2..7b060cc 100644 --- a/lib/rom/csv/gateway.rb +++ b/lib/rom/csv/gateway.rb @@ -45,11 +45,11 @@ class Gateway < ROM::Gateway param :path, reader: :private, - type: Dry::Types['strict.string'] + type: Types::Strict::String param :csv_options, default: proc { {} }, reader: :private, - type: Dry::Types['strict.hash'] + type: Types::Strict::Hash # @api private attr_reader :datasets diff --git a/lib/rom/csv/types.rb b/lib/rom/csv/types.rb new file mode 100644 index 0000000..f496816 --- /dev/null +++ b/lib/rom/csv/types.rb @@ -0,0 +1,9 @@ +require 'rom/types' + +module ROM + module CSV + module Types + include ROM::Types + end + end +end From ebc04746e6d47c2bb22509b0f2c4515861a1e652 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 26 Feb 2017 23:17:59 -0800 Subject: [PATCH 15/18] Explicitly mark command methods private The `execute` method is the only public method exposed by commands at this time. --- lib/rom/csv/commands/create.rb | 2 ++ lib/rom/csv/commands/delete.rb | 2 ++ lib/rom/csv/commands/update.rb | 2 ++ 3 files changed, 6 insertions(+) diff --git a/lib/rom/csv/commands/create.rb b/lib/rom/csv/commands/create.rb index e92030a..62a1406 100644 --- a/lib/rom/csv/commands/create.rb +++ b/lib/rom/csv/commands/create.rb @@ -17,6 +17,8 @@ def execute(tuples) insert_tuples end + private + def insert(tuples) tuples.each { |tuple| dataset << new_row(tuple) } dataset.sync! diff --git a/lib/rom/csv/commands/delete.rb b/lib/rom/csv/commands/delete.rb index 8afed3b..944f751 100644 --- a/lib/rom/csv/commands/delete.rb +++ b/lib/rom/csv/commands/delete.rb @@ -19,6 +19,8 @@ def execute dataset.data end + private + def dataset relation.dataset end diff --git a/lib/rom/csv/commands/update.rb b/lib/rom/csv/commands/update.rb index e1144c0..8979ec5 100644 --- a/lib/rom/csv/commands/update.rb +++ b/lib/rom/csv/commands/update.rb @@ -14,6 +14,8 @@ def execute(tuple) update(tuple) end + private + def update(tuple) original_data = original_dataset.to_a output = [] From 1ce894870c82a2d856745f86f6e11a0ef6c72cc1 Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 26 Feb 2017 23:19:06 -0800 Subject: [PATCH 16/18] Update `Relation` to subclass `Memory::Relation` Recent changes to `ROM::Memory::Relation` made it possible to use it as the superclass for adapter relations. This adds schema support among other things. --- lib/rom/csv/relation.rb | 6 ++---- spec/integration/gateway_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/rom/csv/relation.rb b/lib/rom/csv/relation.rb index 8871e86..d02cd6d 100644 --- a/lib/rom/csv/relation.rb +++ b/lib/rom/csv/relation.rb @@ -1,4 +1,4 @@ -require 'rom/relation' +require 'rom/memory' module ROM module CSV @@ -9,11 +9,9 @@ module CSV # end # # @api public - class Relation < ROM::Relation + class Relation < ROM::Memory::Relation adapter :csv - forward :join, :project, :restrict, :order - def count dataset.count end diff --git a/spec/integration/gateway_spec.rb b/spec/integration/gateway_spec.rb index e4a80ba..8b2e87f 100644 --- a/spec/integration/gateway_spec.rb +++ b/spec/integration/gateway_spec.rb @@ -9,6 +9,12 @@ configuration.relation(:users) do gateway :users + schema(:users) do + attribute :user_id, Types::Strict::Int + attribute :name, Types::Strict::String + attribute :email, Types::Strict::String + end + def by_name(name) restrict(name: name) end From c95b75b879399b3f05bcf176c8644293cc36264f Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 5 Mar 2017 19:59:33 -0800 Subject: [PATCH 17/18] Extract csv file handling to `CSV::Storage` Adds `CSV::Storage` to perform the reading and writing to csv files. The new class is used in the `Dataset` and the `Gateway`. The connection is passed in to the dataset to keep the command functionality working. --- lib/rom/csv/dataset.rb | 33 ++++++++++++--------------- lib/rom/csv/gateway.rb | 11 +++------ lib/rom/csv/storage.rb | 52 ++++++++++++++++++++++++++++++++++++++++++ lib/rom/csv/types.rb | 4 ++++ 4 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 lib/rom/csv/storage.rb diff --git a/lib/rom/csv/dataset.rb b/lib/rom/csv/dataset.rb index 838df77..533066c 100644 --- a/lib/rom/csv/dataset.rb +++ b/lib/rom/csv/dataset.rb @@ -1,17 +1,24 @@ require 'rom/memory/dataset' +require 'rom/csv/storage' module ROM module CSV + # Type definition used to constrain the `connection` option + StorageType = Types.Definition(Storage).constrained(type: Storage) + # Dataset for CSV # # @api public class Dataset < ROM::Memory::Dataset - option :path, + + # Connection to the file + # + # @return [Storage] + # + # @api private + option :connection, optional: true, - type: Types::Strict::String - option :file_options, - default: proc { {} }, - type: Types::Strict::Hash + type: StorageType # Convert each CSV::Row to a hash # @@ -21,23 +28,11 @@ def self.row_proc end def reload! - @data = load_data + @data = connection.load end def sync! - write_data && reload! - end - - def write_data - ::CSV.open(path, 'wb', file_options) do |csv| - data.to_a.each do |tuple| - csv << tuple - end - end - end - - def load_data - ::CSV.table(path, file_options).by_row! + connection.dump(data) && reload! end def count diff --git a/lib/rom/csv/gateway.rb b/lib/rom/csv/gateway.rb index 7b060cc..9ef86a2 100644 --- a/lib/rom/csv/gateway.rb +++ b/lib/rom/csv/gateway.rb @@ -2,6 +2,7 @@ require 'rom/initializer' require 'rom/csv/dataset' require 'rom/csv/commands' +require 'rom/csv/storage' # Ruby Object Mapper # @@ -72,7 +73,7 @@ class Gateway < ROM::Gateway def initialize(*) super @datasets = {} - @connection = ::CSV.table(path, csv_options).by_row! + @connection = Storage.new(path, csv_options) end # Return dataset with the given name @@ -94,7 +95,7 @@ def [](name) # # @api public def dataset(name) - datasets[name] = Dataset.new(connection, dataset_options) + datasets[name] = Dataset.new(connection.load, connection: connection) end # Check if dataset exists @@ -105,12 +106,6 @@ def dataset(name) def dataset?(name) datasets.key?(name) end - - private - - def dataset_options - { path: path, file_options: csv_options } - end end end end diff --git a/lib/rom/csv/storage.rb b/lib/rom/csv/storage.rb new file mode 100644 index 0000000..8c3b888 --- /dev/null +++ b/lib/rom/csv/storage.rb @@ -0,0 +1,52 @@ +require 'csv' +require 'rom/initializer' + +module ROM + module CSV + # CSV file storage for datasets + # + # @api private + class Storage + extend Initializer + + # Path to the file + # + # @return [String] + # + # @api private + param :path, + type: Types::Strict::String + + # Options for file passed to `CSV` + # + # @return [Hash] + # + # @api private + param :csv_options, + default: proc { {} }, + type: Types::Strict::Hash + + # Dump the data to the file at `path` + # + # @return [undefined] + # + # @api public + def dump(data) + ::CSV.open(path, 'wb', csv_options) do |csv| + data.to_a.each do |tuple| + csv << tuple + end + end + end + + # Load the data from the file at `path` + # + # @return [CSV::Table] + # + # @api public + def load + ::CSV.table(path, csv_options).by_row! + end + end + end +end diff --git a/lib/rom/csv/types.rb b/lib/rom/csv/types.rb index f496816..8b939d6 100644 --- a/lib/rom/csv/types.rb +++ b/lib/rom/csv/types.rb @@ -4,6 +4,10 @@ module ROM module CSV module Types include ROM::Types + + def self.Definition(primitive) + Dry::Types::Definition.new(primitive) + end end end end From 983fc7b3f854770515df0686e322f9e62e8137de Mon Sep 17 00:00:00 2001 From: Don Morrison Date: Sun, 5 Mar 2017 20:13:54 -0800 Subject: [PATCH 18/18] Add support for rom-repository --- Gemfile | 1 + lib/rom/csv/relation.rb | 2 ++ spec/integration/gateway_spec.rb | 14 ++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/Gemfile b/Gemfile index 2fd9843..09c5c7d 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ gemspec group :test do gem 'dry-struct' gem 'rom', git: 'https://github.com/rom-rb/rom.git', branch: 'master' + gem 'rom-repository', git: 'https://github.com/rom-rb/rom-repository', branch: 'master' gem 'rspec', '~> 3.1' gem 'codeclimate-test-reporter', require: false gem 'inflecto' diff --git a/lib/rom/csv/relation.rb b/lib/rom/csv/relation.rb index d02cd6d..5a15517 100644 --- a/lib/rom/csv/relation.rb +++ b/lib/rom/csv/relation.rb @@ -1,4 +1,5 @@ require 'rom/memory' +require 'rom/plugins/relation/key_inference' module ROM module CSV @@ -11,6 +12,7 @@ module CSV # @api public class Relation < ROM::Memory::Relation adapter :csv + use :key_inference def count dataset.count diff --git a/spec/integration/gateway_spec.rb b/spec/integration/gateway_spec.rb index 8b2e87f..dcfa1bc 100644 --- a/spec/integration/gateway_spec.rb +++ b/spec/integration/gateway_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' require 'dry-struct' +require 'rom/repository' describe 'CSV gateway' do context 'without extra options' do @@ -135,5 +136,18 @@ class UserWithAddress < Dry::Struct expect(user[:email]).to eql('zolw@example.com') end end + + describe 'with a repository' do + let(:repo) do + Class.new(ROM::Repository[:users]).new(container) + end + + it 'auto-maps to structs' do + user = repo.users.first + + expect(user.name).to eql('Julie') + expect(user.email).to eql('julie.andrews@example.com') + end + end end end