rom-rails 0.2.1 → 0.3.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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