rom-rails 0.2.1 → 0.3.0.beta1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +26 -0
  3. data/CHANGELOG.md +17 -0
  4. data/README.md +4 -0
  5. data/lib/generators/rom/commands/templates/create.rb.erb +10 -0
  6. data/lib/generators/rom/commands/templates/delete.rb.erb +7 -0
  7. data/lib/generators/rom/commands/templates/update.rb.erb +10 -0
  8. data/lib/generators/rom/commands_generator.rb +33 -5
  9. data/lib/generators/rom/form/templates/edit_form.rb.erb +24 -0
  10. data/lib/generators/rom/form/templates/new_form.rb.erb +24 -0
  11. data/lib/generators/rom/form_generator.rb +39 -0
  12. data/lib/generators/rom/mapper/templates/mapper.rb.erb +9 -11
  13. data/lib/generators/rom/mapper_generator.rb +11 -1
  14. data/lib/generators/rom/relation/templates/relation.rb.erb +3 -2
  15. data/lib/generators/rom/relation_generator.rb +16 -1
  16. data/lib/rom-rails.rb +0 -5
  17. data/lib/rom/model.rb +7 -90
  18. data/lib/rom/rails/active_record/configuration.rb +105 -0
  19. data/lib/rom/rails/configuration.rb +4 -27
  20. data/lib/rom/rails/model/form.rb +59 -0
  21. data/lib/rom/rails/model/form/dsl.rb +173 -0
  22. data/lib/rom/rails/model/params.rb +72 -0
  23. data/lib/rom/rails/model/validator.rb +74 -0
  24. data/lib/rom/rails/model/validator/uniqueness_validator.rb +39 -0
  25. data/lib/rom/rails/railtie.rb +65 -29
  26. data/lib/rom/rails/version.rb +1 -1
  27. data/rom-rails.gemspec +3 -1
  28. data/spec/dummy/app/commands/tasks.rb +5 -0
  29. data/spec/dummy/app/commands/users.rb +4 -10
  30. data/spec/dummy/app/controllers/users_controller.rb +30 -0
  31. data/spec/dummy/app/forms/new_user_form.rb +9 -0
  32. data/spec/dummy/app/forms/update_user_form.rb +9 -0
  33. data/spec/dummy/app/forms/user_form.rb +15 -0
  34. data/spec/dummy/app/mappers/users.rb +6 -6
  35. data/spec/dummy/app/relations/tasks.rb +9 -0
  36. data/spec/dummy/app/relations/users.rb +5 -1
  37. data/spec/dummy/app/views/users/edit.html.erb +6 -0
  38. data/spec/dummy/app/views/users/new.html.erb +6 -0
  39. data/spec/dummy/config/routes.rb +5 -1
  40. data/spec/dummy/db/migrate/20141110205016_add_users.rb +2 -0
  41. data/spec/dummy/db/migrate/20150202194440_create_tasks.rb +7 -0
  42. data/spec/dummy/db/schema.rb +8 -8
  43. data/spec/dummy/spec/features/users_spec.rb +30 -0
  44. data/spec/dummy/spec/integration/logger_spec.rb +1 -1
  45. data/spec/dummy/spec/integration/user_commands_spec.rb +2 -18
  46. data/spec/dummy/spec/integration/user_model_mapping_spec.rb +2 -2
  47. data/spec/dummy/spec/integration/user_params_spec.rb +30 -15
  48. data/spec/lib/active_record/configuration_spec.rb +98 -0
  49. data/spec/lib/generators/commands_generator_spec.rb +54 -14
  50. data/spec/lib/generators/form_generator_spec.rb +89 -0
  51. data/spec/lib/generators/mapper_generator_spec.rb +10 -12
  52. data/spec/lib/generators/relation_generator_spec.rb +16 -6
  53. data/spec/spec_helper.rb +1 -1
  54. data/spec/unit/form_spec.rb +297 -0
  55. data/spec/unit/{model_spec.rb → params_spec.rb} +0 -0
  56. data/spec/unit/validator_spec.rb +75 -0
  57. metadata +72 -20
  58. data/lib/generators/rom/commands/templates/commands.rb.erb +0 -15
  59. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  60. data/spec/dummy/app/params/user_params.rb +0 -7
  61. data/spec/dummy/app/validators/user_validator.rb +0 -3
@@ -0,0 +1,74 @@
1
+ require 'rom/rails/model/validator/uniqueness_validator'
2
+
3
+ module ROM
4
+ module Model
5
+ # Mixin for ROM-compliant validator objects
6
+ #
7
+ # @example
8
+ #
9
+ #
10
+ # class UserParams
11
+ # include ROM::Model::Params
12
+ #
13
+ # attribute :name
14
+ #
15
+ # validates :name, presence: true
16
+ # end
17
+ #
18
+ # class UserValidator
19
+ # include ROM::Model::Validator
20
+ # end
21
+ #
22
+ # params = UserParams.new(name: '')
23
+ # UserValidator.call(params) # raises ValidationError
24
+ #
25
+ # @api public
26
+ module Validator
27
+ def self.included(base)
28
+ base.class_eval do
29
+ extend ClassMethods
30
+ include ActiveModel::Validations
31
+ include Equalizer.new(:params, :errors)
32
+ end
33
+ end
34
+
35
+ attr_reader :params
36
+ delegate :model_name, to: :params
37
+
38
+ def initialize(params)
39
+ @params = params
40
+ end
41
+
42
+ def to_model
43
+ params
44
+ end
45
+
46
+ def call
47
+ raise ValidationError, errors unless valid?
48
+ params
49
+ end
50
+
51
+ private
52
+
53
+ def method_missing(name)
54
+ params[name]
55
+ end
56
+
57
+ module ClassMethods
58
+ def relation(name = nil)
59
+ @relation = name if name
60
+ @relation
61
+ end
62
+
63
+ def model_name
64
+ params.model_name
65
+ end
66
+
67
+ def call(params)
68
+ validator = new(params)
69
+ validator.call
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_model/validator'
2
+
3
+ module ROM
4
+ module Model
5
+ module Validator
6
+ class UniquenessValidator < ActiveModel::EachValidator
7
+ attr_reader :klass, :message
8
+
9
+ def initialize(options)
10
+ super
11
+ @klass = options.fetch(:class)
12
+ @message = options.fetch(:message) { :taken }
13
+ end
14
+
15
+ def validate_each(validator, name, value)
16
+ validator.errors.add(name, message) unless unique?(name, value)
17
+ end
18
+
19
+ private
20
+
21
+ def relation
22
+ rom.relations[relation_name]
23
+ end
24
+
25
+ def relation_name
26
+ klass.relation
27
+ end
28
+
29
+ def rom
30
+ ROM.env
31
+ end
32
+
33
+ def unique?(name, value)
34
+ relation.unique?(name => value)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -3,56 +3,92 @@ require 'rails'
3
3
  require 'rom/rails/inflections'
4
4
  require 'rom/rails/configuration'
5
5
  require 'rom/rails/controller_extension'
6
+ require 'rom/rails/active_record/configuration'
6
7
 
7
- if defined?(Spring)
8
- Spring.after_fork { ROM::Rails::Railtie.disconnect }
9
- end
8
+ Spring.after_fork { ROM::Rails::Railtie.disconnect } if defined?(Spring)
10
9
 
11
10
  module ROM
12
11
  module Rails
13
12
  class Railtie < ::Rails::Railtie
14
- def self.disconnect
15
- return unless ROM.env
16
-
17
- ROM.env.repositories.each_value do |repository|
18
- repository.adapter.disconnect
13
+ initializer 'rom.configure_action_controller' do
14
+ ActiveSupport.on_load(:action_controller) do
15
+ ActionController::Base.send(:include, ControllerExtension)
19
16
  end
20
17
  end
21
18
 
22
- def self.load_all
23
- %w(relations mappers commands).each do |type|
24
- load_files(type, ::Rails.root)
19
+ initializer 'rom.adjust_eager_load_paths' do |app|
20
+ paths = %w(commands mappers relations).map do |directory|
21
+ ::Rails.root.join('app', directory).to_s
25
22
  end
23
+
24
+ app.config.eager_load_paths -= paths
26
25
  end
27
26
 
28
- def self.load_files(type, root)
29
- Dir[root.join("app/#{type}/**/*.rb").to_s].each do |path|
30
- load(path)
31
- end
27
+ # Make `ROM::Rails::Configuration` instance available to the user via
28
+ # `Rails.application.config` before other initializers run.
29
+ config.before_initialize do |_app|
30
+ config.rom = Configuration.new
32
31
  end
33
32
 
34
- initializer "rom.configure" do |app|
35
- config.rom = Configuration.build(app)
33
+ # Reload ROM-related application code on each request.
34
+ config.to_prepare do |_config|
35
+ Railtie.setup
36
36
  end
37
37
 
38
- initializer "rom.load_schema" do |_app|
39
- require schema_file if schema_file.exist?
38
+ # Behaves like `Railtie#configure` if the given block does not take any
39
+ # arguments. Otherwise yields the ROM configuration to the block.
40
+ #
41
+ # @example
42
+ # ROM::Rails::Railtie.configure do |config|
43
+ # config.repositories[:yaml] = {uri: 'yaml:///data'}
44
+ # end
45
+ #
46
+ # @api public
47
+ def configure(&block)
48
+ if block.arity == 1
49
+ block.call(config.rom)
50
+ else
51
+ super
52
+ end
40
53
  end
41
54
 
42
- initializer "rom:prepare" do |app|
43
- config.to_prepare do |_config|
44
- Railtie.disconnect
45
- app.config.rom.setup!
46
- app.config.rom.load!
47
- app.config.rom.finalize!
48
- ActionController::Base.send(:include, ControllerExtension)
55
+ def disconnect
56
+ # TODO: Add `ROM.env.disconnect` to core.
57
+ ROM.env.repositories.each_value(&:disconnect)
58
+ end
59
+
60
+ def setup
61
+ if ROM.env
62
+ ROM.setup(ROM.env.repositories)
63
+ else
64
+ repositories = config.rom.repositories
65
+
66
+ # If there's no default repository configured, try to infer it from
67
+ # other sources, e.g. ActiveRecord.
68
+ repositories[:default] ||= infer_default_repository
69
+
70
+ ROM.setup(repositories.symbolize_keys)
49
71
  end
72
+ load_all
73
+ ROM.finalize
74
+ end
75
+
76
+ def infer_default_repository
77
+ return unless defined?(::ActiveRecord)
78
+ spec = ActiveRecord::Configuration.call
79
+ [:sql, spec[:uri], spec[:options]]
50
80
  end
51
81
 
52
- private
82
+ def load_all
83
+ %w(relations mappers commands).each do |type|
84
+ load_files(type)
85
+ end
86
+ end
53
87
 
54
- def schema_file
55
- root.join('db/rom/schema.rb')
88
+ def load_files(type)
89
+ Dir[root.join("app/#{type}/**/*.rb").to_s].each do |path|
90
+ require_dependency(path)
91
+ end
56
92
  end
57
93
 
58
94
  def root
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  module Rails
3
- VERSION = "0.2.1".freeze
3
+ VERSION = "0.3.0.beta1".freeze
4
4
  end
5
5
  end
data/rom-rails.gemspec CHANGED
@@ -17,7 +17,9 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_runtime_dependency 'rom', '~> 0.5', '>= 0.5.0'
20
+ spec.add_runtime_dependency 'rom', '~> 0.6.0.beta1'
21
+ spec.add_runtime_dependency 'addressable', '~> 2.3'
22
+ spec.add_runtime_dependency 'charlatan', '~> 0.1'
21
23
  spec.add_runtime_dependency 'virtus', '~> 1.0', '>= 1.0.4'
22
24
  spec.add_runtime_dependency 'railties', ['>= 3.0', '< 5.0']
23
25
 
@@ -0,0 +1,5 @@
1
+ class CreateTask < ROM::Commands::Create[:sql]
2
+ relation :tasks
3
+ register_as :create
4
+ result :one
5
+ end
@@ -1,11 +1,5 @@
1
- ROM.commands(:users) do
2
- define(:create) do
3
- input UserParams
4
- validator UserValidator
5
- result :one
6
- end
7
-
8
- define(:delete) do
9
- result :one
10
- end
1
+ class DeleteUser < ROM::Commands::Delete[:sql]
2
+ relation :users
3
+ register_as :delete
4
+ result :one
11
5
  end
@@ -10,6 +10,36 @@ class UsersController < ApplicationController
10
10
  render
11
11
  end
12
12
 
13
+ def new
14
+ render :new, locals: { user: NewUserForm.build }
15
+ end
16
+
17
+ def create
18
+ user_form = NewUserForm.build(params[:user]).save
19
+
20
+ if user_form.success?
21
+ redirect_to :users
22
+ else
23
+ render :new, locals: { user: user_form }
24
+ end
25
+ end
26
+
27
+ def edit
28
+ user_form = UpdateUserForm.build({}, { id: params[:id] })
29
+
30
+ render :edit, locals: { user: user_form }
31
+ end
32
+
33
+ def update
34
+ user_form = UpdateUserForm.build(params[:user], id: params[:id]).save
35
+
36
+ if user_form.success?
37
+ redirect_to :users
38
+ else
39
+ render :edit, locals: { user: user_form }
40
+ end
41
+ end
42
+
13
43
  def search
14
44
  render :index
15
45
  end
@@ -0,0 +1,9 @@
1
+ class NewUserForm < UserForm
2
+ commands users: :create
3
+
4
+ params.timestamps(:created_at)
5
+
6
+ def commit!
7
+ users.try { users.create.call(params) }
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class UpdateUserForm < UserForm
2
+ commands users: :update
3
+
4
+ params.timestamps(:updated_at)
5
+
6
+ def commit!
7
+ users.try { users.update.by_id(id).set(params) }
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ class UserForm < ROM::Model::Form
2
+ input do
3
+ set_model_name 'User'
4
+
5
+ attribute :name, String
6
+ attribute :email, String
7
+ end
8
+
9
+ validations do
10
+ relation :users
11
+
12
+ validates :name, :email, presence: true
13
+ validates :email, uniqueness: true
14
+ end
15
+ end
@@ -1,8 +1,8 @@
1
- ROM.mappers do
2
- define(:users) do
3
- model User
1
+ class UserMapper < ROM::Mapper
2
+ relation :users
4
3
 
5
- attribute :id
6
- attribute :name
7
- end
4
+ model User
5
+
6
+ attribute :id
7
+ attribute :name
8
8
  end
@@ -0,0 +1,9 @@
1
+ class Tasks < ROM::Relation[:sql]
2
+ def by_id(id)
3
+ where(id: id)
4
+ end
5
+
6
+ def all
7
+ select(:id, :title).order(:id)
8
+ end
9
+ end
@@ -1,4 +1,8 @@
1
- ROM.relation(:users) do
1
+ class Users < ROM::Relation[:sql]
2
+ def by_id(id)
3
+ where(id: id)
4
+ end
5
+
2
6
  def index
3
7
  order(:name)
4
8
  end
@@ -0,0 +1,6 @@
1
+ <%= form_for user do |f| %>
2
+ <%= user.errors.full_messages if user.errors %>
3
+ <%= f.text_field :email %>
4
+ <%= f.text_field :name %>
5
+ <%= f.submit %>
6
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <%= form_for user do |f| %>
2
+ <%= user.errors.full_messages if user.errors %>
3
+ <%= f.text_field :email %>
4
+ <%= f.text_field :name %>
5
+ <%= f.submit %>
6
+ <% end %>
@@ -1,5 +1,9 @@
1
1
  Dummy::Application.routes.draw do
2
- get '/users' => 'users#index'
2
+ get '/users' => 'users#index', as: :users
3
+ post '/users' => 'users#create'
4
+ get '/users/:id/edit' => 'users#edit'
5
+ patch '/users/:id' => 'users#update', as: :user
6
+ get '/users/new' => 'users#new'
3
7
  get '/users/search' => 'users#search'
4
8
  get '/users/ping' => 'users#ping'
5
9
  end
@@ -2,6 +2,8 @@ class AddUsers < ActiveRecord::Migration
2
2
  def change
3
3
  create_table(:users) do |t|
4
4
  t.string :name
5
+ t.string :email
6
+ t.timestamps
5
7
  end
6
8
  end
7
9
  end
@@ -0,0 +1,7 @@
1
+ class CreateTasks < ActiveRecord::Migration
2
+ def change
3
+ create_table :tasks do |t|
4
+ t.string :title
5
+ end
6
+ end
7
+ end