potter 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/.ruby-version +1 -0
  4. data/README.md +39 -14
  5. data/Rakefile +7 -3
  6. data/db/migrate/20230109071230_create_requests.rb +48 -0
  7. data/lib/potter/application_record.rb +10 -0
  8. data/lib/potter/ast/binary.rb +35 -0
  9. data/lib/potter/ast/dsl.rb +9 -0
  10. data/lib/potter/ast/node.rb +37 -0
  11. data/lib/potter/ast/proxy.rb +14 -0
  12. data/lib/potter/ast/unary.rb +21 -0
  13. data/lib/potter/ast/value.rb +13 -0
  14. data/lib/potter/ast/variable.rb +11 -0
  15. data/lib/potter/ast.rb +25 -0
  16. data/lib/potter/caching.rb +33 -0
  17. data/lib/potter/collections.rb +29 -0
  18. data/lib/potter/core_ext/active_record.rb +17 -0
  19. data/lib/potter/core_ext.rb +7 -0
  20. data/lib/potter/csv.rb +17 -0
  21. data/lib/potter/endpoints.rb +8 -0
  22. data/lib/potter/engine.rb +15 -0
  23. data/lib/potter/enum.rb +12 -15
  24. data/lib/potter/field.rb +34 -0
  25. data/lib/potter/flag.rb +1 -3
  26. data/lib/potter/{endpoint.rb → index.rb} +1 -1
  27. data/lib/potter/information_schema/base.rb +11 -0
  28. data/lib/potter/information_schema/index.rb +9 -0
  29. data/lib/potter/information_schema/table.rb +7 -0
  30. data/lib/potter/migrations.rb +42 -0
  31. data/lib/potter/model.rb +16 -0
  32. data/lib/potter/option_helpers.rb +40 -0
  33. data/lib/potter/record.rb +46 -0
  34. data/lib/potter/request.rb +92 -0
  35. data/lib/potter/response.rb +21 -0
  36. data/lib/potter/schema.rb +58 -0
  37. data/lib/potter/transformer.rb +15 -0
  38. data/lib/potter/transformers.rb +28 -0
  39. data/lib/potter/type.rb +26 -0
  40. data/lib/potter/types.rb +66 -0
  41. data/lib/potter/version.rb +1 -1
  42. data/lib/potter.rb +26 -3
  43. metadata +123 -11
  44. data/.rspec +0 -3
  45. data/lib/potter/resource.rb +0 -15
  46. data/sig/potter.rbs +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd356cc421ff92e92441fd7539517877c1d969e4e772b44fc437e043c788c547
4
- data.tar.gz: c25a9ff7843b5d13f91d791ec3169bdebbf9e2de1a073108044b37db21e0d856
3
+ metadata.gz: a23406491aa285780e485ed61cfe1fb6853cd975d6319bf9b3d940fff9202a5d
4
+ data.tar.gz: 7d25cad13739d71f78f8faabdbd39cc0f95ec317830a623d4137b13fda108f26
5
5
  SHA512:
6
- metadata.gz: 01f79d0a653082150fb0e2a9de317d9c4212892f6acd7076aef68b8e525178c8df069fe143851c4a37e11afacbff1218089bc692440befa6092933421ace02a6
7
- data.tar.gz: c61ee5de4b9765d4cd7688aebbcf71db66a40c99a866c78406537c199c21cfd27f31e5fa2e2ed1bbc2addd77d9fe99caa33bffe2f5ad071782c8a37ab810c71e
6
+ metadata.gz: deab47dc584ba58d04dd04423aac1ca28f841693faaa7fde01613080f0f360c560d59421fa4703775139aa5008451acb1a3b206783a01ace9870040cd710d73c
7
+ data.tar.gz: 57f40e4752edda31d00b4f3cdc79178d2519f75b61965fe65cce0b65d50359ae1a0347e8c969dcfd6067b0e560cbbe9036fe52c8aa8ed50de2d1f6ea8927036c
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ inherit_gem:
2
+ cnc: rubocop.yml
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 4.0.1
data/README.md CHANGED
@@ -1,39 +1,64 @@
1
- # Potter
1
+ # 🏺 Potter
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
3
+ A gem for modeling anything: databases, CSVs, REST APIs, file systems.
4
4
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/potter`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ It's meant to be simple and compact.
6
6
 
7
- ## Installation
8
-
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
7
+ ## 💎 Installation
10
8
 
11
9
  Install the gem and add to the application's Gemfile by executing:
12
10
 
13
11
  ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
12
+ bundle add potter
15
13
  ```
16
14
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
15
+ If bundler is not being used to manage dependencies, install the gem by
16
+ executing:
18
17
 
19
18
  ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
19
+ gem install potter
21
20
  ```
22
21
 
23
22
  ## Usage
24
23
 
25
- TODO: Write usage instructions here
24
+ Start by declaring your models:
25
+
26
+ ```rb
27
+ class Base
28
+ include Potter::API
29
+ root "https://"
30
+ end
31
+
32
+ class Transaction < Base
33
+ date :date
34
+ belongs_to :merchant
35
+ string :category
36
+ string :account
37
+ string :original_statement
38
+ string :notes
39
+ float :amount, scale: 2, note: "positive numbers for income and negative numbers for expenses"
40
+ string :tags
41
+ end
42
+ ```
26
43
 
27
44
  ## Development
28
45
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
46
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
47
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
48
+ prompt that will allow you to experiment.
30
49
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
50
+ To install this gem onto your local machine, run `bundle exec rake install`. To
51
+ release a new version, update the version number in `version.rb`, and then run
52
+ `bundle exec rake release`, which will create a git tag for the version, push
53
+ git commits and the created tag, and push the `.gem` file to
54
+ [rubygems.org](https://rubygems.org).
32
55
 
33
56
  ## Contributing
34
57
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/potter.
58
+ Bug reports and pull requests are welcome on GitHub at
59
+ https://github.com/craft-concept/potter.
36
60
 
37
61
  ## License
38
62
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
63
+ The gem is available as open source under the terms of the
64
+ [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
4
+ require "minitest/test_task"
5
5
 
6
- RSpec::Core::RakeTask.new(:spec)
6
+ Minitest::TestTask.create
7
7
 
8
- task default: :spec
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,48 @@
1
+ class CreateRequests < ActiveRecord::Migration[8.0]
2
+ def adapter
3
+ @adapter ||= ActiveRecord::Base.connection.adapter_name.downcase.then { ActiveSupport::StringInquirer.new _1 }
4
+ end
5
+
6
+ def change
7
+ methods = %w[get post put patch delete head options trace connect]
8
+ states = %w[received queued sending responded failed]
9
+
10
+ unless adapter.sqlite?
11
+ create_enum :http_method, methods
12
+ create_enum :request_state, states
13
+ end
14
+
15
+ create_table :responses do |t|
16
+ t.string :type, null: false
17
+ t.integer :status, null: false
18
+
19
+ t.jsonb :headers
20
+ t.text :body
21
+
22
+ t.timestamps
23
+
24
+ t.check_constraint %(status BETWEEN 0 AND 999), name: "valid_status"
25
+ end
26
+
27
+ create_table :requests do |t|
28
+ t.string :type, null: false, index: true
29
+ t.string :state, null: false
30
+ t.string :http_method, null: false
31
+ t.string :root, null: false, index: true
32
+ t.string :path, null: false, index: true
33
+
34
+ t.jsonb :params
35
+ t.jsonb :headers
36
+ t.text :body
37
+
38
+ t.references :response, foreign_key: true
39
+
40
+ t.timestamps
41
+
42
+ t.check_constraint %{http_method in (#{methods.map { "'#{_1}'" }.join(?,)})}, name: "http_method_enum"
43
+ t.check_constraint %{state in (#{states.map { "'#{_1}'" }.join(?,)})}, name: "state_enum"
44
+ end
45
+
46
+ add_index :requests, %i[type path params http_method]
47
+ end
48
+ end
@@ -0,0 +1,10 @@
1
+ module Potter
2
+ if defined?(::ApplicationRecord)
3
+ ApplicationRecord = ::ApplicationRecord
4
+ else
5
+ require "active_record"
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ primary_abstract_class
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module Potter
2
+ module AST
3
+ class Binary < Node
4
+ def initialize(left, right)
5
+ super()
6
+ @left = left
7
+ @right = right
8
+ end
9
+
10
+ def walk
11
+ yield @left
12
+ yield @right
13
+ end
14
+
15
+ def eval = @left.eval.public_send(symbol, @right.eval)
16
+ end
17
+
18
+ def self.define_binary(const, symbol, name = const.underscore, &)
19
+ define_node(const, Binary, symbol:, name:, &)
20
+ end
21
+
22
+ define_binary :EqualTo, :==, :eq
23
+ define_binary :GreaterThan, :>, :gt
24
+ define_binary :GreaterOrEqual, :>=, :gte
25
+ define_binary :LessThan, :<, :lt
26
+ define_binary :LessOrEqual, :<=, :lte
27
+
28
+ define_binary :And, :&
29
+ define_binary :Or, :|
30
+ define_binary :Plus, :+
31
+ define_binary :Minus, :-
32
+ define_binary :Times, :*
33
+ define_binary :Range, :".."
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ module Potter
2
+ module AST
3
+ module DSL
4
+ module_function
5
+
6
+ def Node(...) = AST.Node(...)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ module Potter
2
+ module AST
3
+ class Node
4
+ def symbol = SYMBOL
5
+ def name = NAME
6
+
7
+ def visit(visitor)
8
+ visitor.try(name, self)
9
+ end
10
+
11
+ def hash = [self.class, *instance_variable_values].hash
12
+
13
+ def eql?(o)
14
+ hash == o.hash
15
+ end
16
+ end
17
+
18
+ def self.define_node(const, parent = Node, symbol: nil, name: const.underscore, &)
19
+ define_class(const, parent) do
20
+ const_set(:SYMBOL, symbol) if symbol
21
+ const_set(:NAME, name) if name
22
+ class_exec(&) if block_given?
23
+ klass = self
24
+
25
+ if name
26
+ Node.define_method(name) {|*args| Proxy klass.new(self, *args.map { Node _1 }) }
27
+ DSL.define_singleton_method(name) {|*args| klass.new(*args.map { Node _1 }) }
28
+ Proxy.define_method(name) {|*args| Proxy klass.new(@node, *args.map { Node _1 }) }
29
+ end
30
+
31
+ if symbol
32
+ Proxy.define_method(symbol) {|*args| Proxy klass.new(@node, *args.map { Node _1 }) }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ module Potter
2
+ module AST
3
+ class Proxy
4
+ attr_reader :node
5
+
6
+ def initialize(node)
7
+ @node = node
8
+ end
9
+
10
+ def Node(o) = AST.Node(o)
11
+ def Proxy(o) = AST.Proxy(o)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ module Potter
2
+ module AST
3
+ class Unary < Node
4
+ attr_reader :child
5
+
6
+ def initialize(child)
7
+ super()
8
+ @child = child
9
+ end
10
+
11
+ def eval = public_send(symbol)
12
+ end
13
+
14
+ def self.define_unary(const, symbol)
15
+ define_node(const, Unary, symbol:)
16
+ end
17
+
18
+ define_unary :Not, :!@
19
+ define_unary :Negate, :-@
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module Potter
2
+ module AST
3
+ class Value < Node
4
+ attr_reader :value
5
+
6
+ def initialize(value)
7
+ @value = value
8
+ end
9
+
10
+ def eval = @value
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module Potter
2
+ module AST
3
+ class Variable < Node
4
+ attr_reader :name
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/potter/ast.rb ADDED
@@ -0,0 +1,25 @@
1
+ module Potter
2
+ module AST
3
+ module_function
4
+
5
+ def Proxy(o)
6
+ o.is_a?(Proxy) ? o : Proxy.new(Node o)
7
+ end
8
+
9
+ def Value(o)
10
+ o.is_a?(Value) ? o : Value.new(o)
11
+ end
12
+
13
+ def Node(o)
14
+ o.is_a?(Node) ? o : Value.new(o)
15
+ end
16
+
17
+ def Variable(o)
18
+ o.is_a?(Variable) ? o : Variable.new(o)
19
+ end
20
+
21
+ def proxy(o) = Proxy(o)
22
+ def value(o) = Value(o)
23
+ def var(o) = Variable(o)
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ module Potter
2
+ concern :Caching do
3
+ # def full = cache(:full) { first + last }
4
+ def cache(name, &)
5
+ name = "@#{name}"
6
+ @_cached ||= []
7
+ @_cached << name
8
+ instance_variable_defined?(name) ?
9
+ instance_variable_get(name) :
10
+ instance_variable_set(name, instance_exec(&))
11
+ end
12
+
13
+ def clear_cache!
14
+ @_cached.each { remove_instance_variable(_1) }.clear
15
+ end
16
+
17
+ class_methods do
18
+ # derive :full, -> { first + last }
19
+ def derive(name, proc = nil, &block)
20
+ proc ||= block
21
+ define_method(name) { cache(name, &proc) }
22
+ end
23
+
24
+ # cache! def full = first + last
25
+ def cache!(name)
26
+ prepend Module.new.tap { _1.module_eval <<~RUBY }
27
+ def #{name} = cache(:#{name}) { super }
28
+ RUBY
29
+ name
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ module Potter
2
+ class Collection < Array
3
+ def self.filter(name, block)
4
+ define_method(name) do |*args|
5
+ filter { _1.instance_exec(*args, &block) }
6
+ end
7
+ end
8
+ end
9
+
10
+ module Collections
11
+ extend ActiveSupport::Concern
12
+
13
+ class_methods do
14
+ def inherited(child)
15
+ super
16
+
17
+ child.class_eval <<-RUBY
18
+ class Collection < Collection
19
+ end
20
+ RUBY
21
+ end
22
+
23
+ def filter(...) = Collection.filter(...)
24
+ def all(arr) = Collection.new(arr)
25
+ def if(cond, ...) = cond ? instance_exec(cond, ...) : self
26
+ def to(...) = map { _1.to(...) }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ if defined? ::ActiveRecord::ConnectionAdapters::SQLite3
2
+ class ::ActiveRecord::ConnectionAdapters::SQLite3::TableDefinition
3
+ unless method_defined? :jsonb
4
+ def jsonb(name)
5
+ blob name
6
+ check_constraint %{json_valid(#{name}, 6)}, name: "#{name}_valid_json"
7
+ end
8
+ end
9
+
10
+ unless method_defined? :json
11
+ def json(name)
12
+ text name
13
+ check_constraint %{json_valid(#{name}, 6)}, name: "#{name}_valid_json"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cnc/core_ext"
4
+
5
+ Dir.glob(File.expand_path("core_ext/*.rb", __dir__)).sort.each do |path|
6
+ require path
7
+ end
data/lib/potter/csv.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "csv"
2
+
3
+ module Potter
4
+ module CSV
5
+ extend ActiveSupport::Concern
6
+
7
+ class Table < ::CSV::Table
8
+ end
9
+
10
+ class Row < ::CSV::Row
11
+ end
12
+
13
+ def to_csv
14
+ Row.new(attributes.keys, attributes.values.map(&:presence))
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module Potter
2
+ class Endpoints
3
+ def self.resources(name, only: nil)
4
+ @resources ||= []
5
+ @resources << {name:, only:}
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ require "rails"
2
+
3
+ module Potter
4
+ class Engine < ::Rails::Engine
5
+ # config.autoload_paths += paths["lib"].to_a
6
+
7
+ initializer "potter.inflections" do
8
+ ActiveSupport::Inflector.inflections do |i|
9
+ i.acronym "AST"
10
+ i.acronym "DSL"
11
+ i.acronym "CSV"
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/potter/enum.rb CHANGED
@@ -1,14 +1,12 @@
1
1
  module Potter
2
- module Enum
3
- def self.included(base)
4
- base.extend ClassMethods
5
- end
2
+ concern :Enum do
3
+ include Transformers
6
4
 
7
- module ClassMethods
8
- def next_id = @last_id ? @last_id += 1 : @last_id = 0
5
+ class_methods do
6
+ def next_id = @prev_id ? @prev_id + 1 : 0
9
7
 
10
8
  def const(name, id = next_id, description: nil)
11
- @last_id = id
9
+ @prev_id = id
12
10
  const_set(name, id)
13
11
  end
14
12
 
@@ -22,15 +20,14 @@ module Potter
22
20
  end
23
21
  end
24
22
 
25
- def self.of(type)
26
- parent = self
27
- name = type.name.gsub("::", "")
28
- const_get(name) ||
29
- const_set(type.name.gsub("::", ""), Class.new(type) do
23
+ def self.of(type, &)
24
+ const_cache(type) do
25
+ parent = self
26
+ Class.new(type) do
30
27
  include parent
31
- end)
28
+ class_exec(&) if block_given?
29
+ end
30
+ end
32
31
  end
33
32
  end
34
-
35
- def self.Enum(type) = Enum.of(type)
36
33
  end
@@ -0,0 +1,34 @@
1
+ module Potter
2
+ class Field
3
+ include OptionHelpers
4
+
5
+ option :name, &:to_sym
6
+ option :type, -> { _1.is_a?(Class) ? _1.new : _1 }
7
+ option :default
8
+
9
+ option :enum
10
+ option :validates
11
+ option :normalizes
12
+
13
+ option :persist?, default: true
14
+ option :required?, default: -> { default.nil? }
15
+ option :declared?
16
+ option :description
17
+
18
+ def initialize(**)
19
+ assign_options!(**)
20
+ end
21
+
22
+ def optional? = !required?
23
+
24
+ def string? = type.type == :string
25
+
26
+ def record!(klass)
27
+ klass.attribute(name, type, default:)
28
+ klass.normalizes(name, **normalizes) if normalizes?
29
+ klass.validates(name, **validates) if validates?
30
+ klass.enum(name, enum) if enum?
31
+ self
32
+ end
33
+ end
34
+ end
data/lib/potter/flag.rb CHANGED
@@ -1,11 +1,9 @@
1
- require_relative "enum"
2
-
3
1
  module Potter
4
2
  module Flag
5
3
  include Enum
6
4
 
7
5
  module ClassMethods
8
- def next_id = @last_id ? @last_id <<= 1 : @last_id = 1
6
+ def next_id = @prev_id ? @prev_id << 1 : 1
9
7
  end
10
8
  end
11
9
 
@@ -1,4 +1,4 @@
1
1
  module Potter
2
- class Endpoint
2
+ class Index
3
3
  end
4
4
  end
@@ -0,0 +1,11 @@
1
+ module Potter
2
+ module InformationSchema
3
+ class Base < ActiveRecord::Base
4
+ # include Potter::Record
5
+ self.abstract_class = true
6
+ self.table_name_prefix = "information_schema."
7
+ self.param_delimiter = "-"
8
+ self.inheritance_column = nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Potter
2
+ module InformationSchema
3
+ class Index < Base
4
+ self.table_name_prefix = nil
5
+ self.table_name = "pg_indexes"
6
+ self.primary_key = "indexname"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Potter
2
+ module InformationSchema
3
+ class Table < Base
4
+ self.primary_key = %w[table_schema table_name]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ module Potter
2
+ module Migrations
3
+ extend ActiveSupport::Concern
4
+
5
+ class Migration < ActiveRecord::Migration[8.0]
6
+ def initialize(model)
7
+ @model = model
8
+ end
9
+
10
+ def column_names
11
+ end
12
+
13
+ def create_or_change_table(name, **, &)
14
+ return create_table(name, **, &) unless table_exists?(name)
15
+
16
+ change_table(name, **) do |t|
17
+ t.remove_columns(*(t.columns.map(&:name) - %w[id]))
18
+ yield t
19
+ end
20
+ end
21
+
22
+ def change
23
+ create_or_change_table(model.table_name) do |t|
24
+ model.fields.each do |f|
25
+ t.column f.name, f.type.to_sym, null: f.optional?
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ included do
32
+ unless defined?(table_name)
33
+ def self.table_name = name.tableize
34
+ end
35
+ end
36
+
37
+ class_methods do
38
+ def migrate! = migration.migrate(:up)
39
+ def migration = Migration.new(self)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ module Potter
2
+ module Model
3
+ extend ActiveSupport::Concern
4
+
5
+ include Schema
6
+ include Collections
7
+ include Transformers
8
+ include CSV
9
+
10
+ included do
11
+ from Array do |values|
12
+ assign values.zip(fields.keys).to_h(&:reverse)
13
+ end
14
+ end
15
+ end
16
+ end