linker 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +17 -0
- data/LICENSE +21 -0
- data/README.md +107 -0
- data/Rakefile +6 -0
- data/lib/linker/forms/attributes.rb +119 -0
- data/lib/linker/forms/configuration_methods.rb +24 -0
- data/lib/linker/forms/params.rb +70 -0
- data/lib/linker/version.rb +3 -0
- data/lib/linker.rb +24 -0
- data/linker.gemspec +30 -0
- data/spec/fake_app/active_record/models.rb +87 -0
- data/spec/fake_app/config/database.yml +9 -0
- data/spec/fake_app/rails_app.rb +27 -0
- data/spec/fake_app/users_form.rb +9 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/users_form_spec.rb +156 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e2ed1e6ea0a1cbeb8e9a03be501ebd955520c170
|
4
|
+
data.tar.gz: 96e64ebd76d12627a1b7cdb9ed6b6a9f9c4e0397
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5023b6abca89663ca60e77d67e78c8ec2d90d618ea2fff75b4f024285e8bba6eba5779f25cb28d1571c38d175aa2336ef1d9321ffbb860d27d698ce89f49991e
|
7
|
+
data.tar.gz: 0550fa1b92021d00cc446ffd72dcc15ddf6b022eff5de7d569371999bdfade9552ed88140483ddde0e60174bb1980de8f077a65c0bece3b68da8d083a86e2f91
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in linker.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
rails_version = ENV["RAILS_VERSION"] || "default"
|
7
|
+
|
8
|
+
rails = case rails_version
|
9
|
+
when "master"
|
10
|
+
{github: "rails/rails"}
|
11
|
+
when "default"
|
12
|
+
"~> 4.1.5"
|
13
|
+
else
|
14
|
+
"~> #{rails_version}"
|
15
|
+
end
|
16
|
+
|
17
|
+
gem "rails", rails
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Glauco Custódio
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Linker [](https://travis-ci.org/glaucocustodio/linker)
|
2
|
+
|
3
|
+
A wrapper to form objects in ActiveRecord. Forget `accepts_nested_attributes_for`.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'linker'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install linker
|
20
|
+
|
21
|
+
## Supported versions
|
22
|
+
- Ruby 2.0+
|
23
|
+
- Rails 3.1+ (including 4.x)
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Given the below model:
|
28
|
+
```ruby
|
29
|
+
class User < ActiveRecord::Base
|
30
|
+
belongs_to :company
|
31
|
+
belongs_to :family
|
32
|
+
|
33
|
+
has_one :address, dependent: :destroy
|
34
|
+
|
35
|
+
has_many :dependent_users, dependent: :destroy
|
36
|
+
has_many :tasks, dependent: :destroy
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
Create a form class, include Linker and set main model:
|
41
|
+
```ruby
|
42
|
+
class UsersForm
|
43
|
+
include Linker
|
44
|
+
|
45
|
+
main_model User # or :user or 'User'
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Now you can create a new form for existing user `UsersForm.new(User.find params[:id])` or to a new one `UsersForm.new(User.new)`:
|
50
|
+
```ruby
|
51
|
+
class UsersController < ApplicationController
|
52
|
+
def new
|
53
|
+
@user_form = UsersForm.new(User.new)
|
54
|
+
end
|
55
|
+
|
56
|
+
def create
|
57
|
+
@user_form.params = users_form_params
|
58
|
+
|
59
|
+
if @user_form.save
|
60
|
+
redirect_to users_path, notice: 'User created successfully'
|
61
|
+
else
|
62
|
+
render :new
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
By default, `save` method will perform validations, you can disable them by passing `validate: false` as parameter.
|
69
|
+
|
70
|
+
Finally, you can use `fields_for` in order to display associated fields.
|
71
|
+
|
72
|
+
```erb
|
73
|
+
<%= form_for @user_form do |f| %>
|
74
|
+
<%= f.text_field :name %>
|
75
|
+
|
76
|
+
<%= f.fields_for :tasks do |ta| %>
|
77
|
+
<%= ta.hidden_field :id %>
|
78
|
+
<%= ta.text_field :name %>
|
79
|
+
|
80
|
+
<%= f.fields_for :company do |co| %>
|
81
|
+
<%= co.hidden_field :id %>
|
82
|
+
<%= co.text_field :name %>
|
83
|
+
<%= co.text_field :website %>
|
84
|
+
<% end %>
|
85
|
+
```
|
86
|
+
|
87
|
+
## How it works
|
88
|
+
|
89
|
+
Linker will map all `has_one`, `has_many` and `belongs_to` associations from main model and let it ready to use.
|
90
|
+
|
91
|
+
Internally, it will include `ActiveModel::Model` if on Rails 4 or `ActiveModel::Validations` if on Rails < 4.
|
92
|
+
|
93
|
+
It will also create `params=` and `save` methods responsible to set new objects and save them. You can override these methods to get a custom behavior without worrying with delegations.
|
94
|
+
|
95
|
+
You can check out a demo project using Linker gem [here](https://github.com/glaucocustodio/linker_demo). [Click here](http://linker-demo.herokuapp.com/) to see it live.
|
96
|
+
|
97
|
+
## Contributing
|
98
|
+
|
99
|
+
1. Fork it
|
100
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
101
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
102
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
103
|
+
5. Create a new Pull Request
|
104
|
+
|
105
|
+
## License
|
106
|
+
|
107
|
+
This projected is licensed under the terms of the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'linker/forms/configuration_methods'
|
2
|
+
|
3
|
+
module Linker
|
4
|
+
module Attributes
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include Linker::ConfigurationMethods
|
7
|
+
|
8
|
+
USELESS_COLUMNS_REGEX = %r{^(updated_at|created_at)$}
|
9
|
+
|
10
|
+
def get_main_model
|
11
|
+
@main_model ||= self.class._main_model.constantize
|
12
|
+
end
|
13
|
+
|
14
|
+
def prepare_attrs
|
15
|
+
get_main_model
|
16
|
+
set_reader_for_main_model
|
17
|
+
set_delegations
|
18
|
+
set_fields_for_methods(map_has_many_associations)
|
19
|
+
set_fields_for_methods(map_belongs_to_associations, true)
|
20
|
+
set_fields_for_methods(map_has_one_associations, true)
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_reader_for_main_model
|
24
|
+
#ap "criando reader for main model #{@main_model.to_s.underscore}"
|
25
|
+
# Create attr reader for main model
|
26
|
+
self.class.__send__(:attr_reader, @main_model.to_s.underscore)
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_delegations
|
30
|
+
# Delegate fields for main model
|
31
|
+
filter_columns(@main_model).each{|c| delegate_attr(c, @main_model.to_s.underscore) }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def get_has_many_associations
|
36
|
+
@hm_assoc ||= @main_model.reflect_on_all_associations(:has_many)
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_has_one_associations
|
40
|
+
@ho_assoc ||= @main_model.reflect_on_all_associations(:has_one)
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_belongs_to_associations
|
44
|
+
@bt_assoc ||= @main_model.reflect_on_all_associations(:belongs_to)
|
45
|
+
end
|
46
|
+
|
47
|
+
def filter_columns model
|
48
|
+
f = model.columns.map(&:name)
|
49
|
+
.delete_if{|cn| USELESS_COLUMNS_REGEX.match(cn) }
|
50
|
+
# Get Paperclip attachments
|
51
|
+
begin
|
52
|
+
f = Paperclip::AttachmentRegistry.names_for(model).inject(f) do |t, c|
|
53
|
+
t << c.to_s
|
54
|
+
end
|
55
|
+
rescue
|
56
|
+
end
|
57
|
+
f
|
58
|
+
end
|
59
|
+
|
60
|
+
def delegate_attr att, class_to
|
61
|
+
#ap "delegando #{att} e #{att}= para #{class_to.underscore.pluralize.to_sym}"
|
62
|
+
self.class.__send__(:delegate, att, "#{att}=", to: class_to.underscore.to_sym)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create required methods to use `fields_for`
|
66
|
+
def set_fields_for_methods assoc_set, singular = false
|
67
|
+
assoc_set.each do |c|
|
68
|
+
method_prefix = singular ? c[:tablelized_klass].singularize : c[:tablelized_klass]
|
69
|
+
|
70
|
+
#ap "criando método #{method_prefix}"
|
71
|
+
self.class.send(:define_method, method_prefix) do
|
72
|
+
assocs = instance_variable_get("@#{get_main_model.to_s.underscore}")
|
73
|
+
.send(method_prefix)
|
74
|
+
|
75
|
+
neww = singular ? c[:klass].constantize.new : [c[:klass].constantize.new] * 2
|
76
|
+
|
77
|
+
if singular
|
78
|
+
(assocs.present? && assocs) || neww
|
79
|
+
else
|
80
|
+
(assocs.map{|c| c}.present? && assocs.map{|c| c}) || neww
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#ap "criando método #{method_prefix}_attributes="
|
85
|
+
self.class.send(:define_method, "#{method_prefix}_attributes=") do |attributes|
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def map_associations assoc
|
91
|
+
assoc.inject([]) do |t, c|
|
92
|
+
t << {
|
93
|
+
klass: c.klass.name,
|
94
|
+
tablelized_klass: c.klass.name.pluralize.underscore,
|
95
|
+
# delete_if remove useless attrs
|
96
|
+
columns: filter_columns(c.klass)
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def map_has_many_associations
|
102
|
+
# Create an array with associated classes names and attrs
|
103
|
+
@mapped_hm_assoc ||= map_associations(get_has_many_associations)
|
104
|
+
@mapped_hm_assoc
|
105
|
+
end
|
106
|
+
|
107
|
+
def map_belongs_to_associations
|
108
|
+
# Create an array with associated classes names and attrs
|
109
|
+
@mapped_bt_assoc ||= map_associations(get_belongs_to_associations)
|
110
|
+
@mapped_bt_assoc
|
111
|
+
end
|
112
|
+
|
113
|
+
def map_has_one_associations
|
114
|
+
# Create an array with associated classes names and attrs
|
115
|
+
@mapped_ho_assoc ||= map_associations(get_has_one_associations)
|
116
|
+
@mapped_ho_assoc
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Linker
|
2
|
+
module ConfigurationMethods
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def main_model main_model
|
7
|
+
@main_model = main_model.to_s.camelize
|
8
|
+
end
|
9
|
+
|
10
|
+
def _main_model
|
11
|
+
@main_model
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
included do
|
16
|
+
if Rails.version.to_i >= 4
|
17
|
+
include ActiveModel::Model
|
18
|
+
else
|
19
|
+
include ActiveModel::Validations
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'linker/forms/attributes'
|
2
|
+
|
3
|
+
module Linker
|
4
|
+
module Params
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
def params= params
|
9
|
+
# Delete all associated objects if there is nothing in params
|
10
|
+
@mapped_hm_assoc.each do |c|
|
11
|
+
_get_main_model.send(c[:tablelized_klass]).destroy_all if !params.key?("#{c[:tablelized_klass]}_attributes")
|
12
|
+
end
|
13
|
+
|
14
|
+
params.each do |param, value|
|
15
|
+
table = param.gsub(%r{_attributes$}, '')
|
16
|
+
|
17
|
+
if value.is_a?(Hash)
|
18
|
+
# belongs_to attrs
|
19
|
+
if map_belongs_to_associations.select{|c| c[:klass] == table.singularize.camelize}.present?
|
20
|
+
if value['id'].present?
|
21
|
+
_get_main_model.send(table).update_attributes(value)
|
22
|
+
else
|
23
|
+
_get_main_model.send("build_#{table}", value)
|
24
|
+
end
|
25
|
+
|
26
|
+
# has_one attrs
|
27
|
+
elsif map_has_one_associations.select{|c| c[:klass] == table.camelize}.present?
|
28
|
+
if value['id'].present?
|
29
|
+
_get_main_model.send(table).update_attributes(value)
|
30
|
+
else
|
31
|
+
_get_main_model.send("#{table}=", table.camelize.constantize.new(value))
|
32
|
+
end
|
33
|
+
|
34
|
+
# has_many attrs
|
35
|
+
else
|
36
|
+
ids = value.map.with_index{|c,i| c.last['id'].present? ? c.last['id'] : nil }.compact
|
37
|
+
_get_main_model.send(table).where(["#{table}.id NOT IN (?)", ids]).destroy_all if ids.present?
|
38
|
+
|
39
|
+
value.each do |c|
|
40
|
+
if c.last['id'].present?
|
41
|
+
_get_main_model.send(table).find(c.last['id']).update_attributes(c.last)
|
42
|
+
else
|
43
|
+
_get_main_model.send(table) << table.singularize.camelize.constantize.new(c.last)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
else
|
48
|
+
self.send("#{param}=", value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def save validate: true
|
54
|
+
main_model = _get_main_model
|
55
|
+
|
56
|
+
if validate
|
57
|
+
self.valid? && main_model.save
|
58
|
+
else
|
59
|
+
main_model.save
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def _get_main_model
|
67
|
+
main_model ||= self.send(self.class._main_model.underscore)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/linker.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "linker/version"
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'linker/forms/attributes'
|
4
|
+
require 'linker/forms/params'
|
5
|
+
|
6
|
+
module Linker
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include Linker::Attributes
|
9
|
+
include Linker::Params
|
10
|
+
|
11
|
+
def initialize main_model_instance
|
12
|
+
# Creating instance variable for main model
|
13
|
+
instance_variable_set("@#{main_model_instance.class.name.underscore}", main_model_instance)
|
14
|
+
|
15
|
+
prepare_attrs
|
16
|
+
end
|
17
|
+
|
18
|
+
included do
|
19
|
+
# allow use form instance variable in form_for. Ie: form_for(@user_form)
|
20
|
+
def to_model
|
21
|
+
instance_variable_get("@#{self.class._main_model.downcase}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/linker.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'linker/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "linker"
|
8
|
+
spec.version = Linker::VERSION
|
9
|
+
spec.authors = ["Glauco Custódio"]
|
10
|
+
spec.email = ["glauco.custodio@gmail.com"]
|
11
|
+
spec.summary = %q{A wrapper to form objects in ActiveRecord. Forget accepts_nested_attributes_for.}
|
12
|
+
spec.description = %q{A wrapper to form objects in ActiveRecord. Forget accepts_nested_attributes_for.}
|
13
|
+
spec.homepage = "https://github.com/glaucocustodio/linker"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'activesupport', ">= 3.1"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
spec.add_development_dependency 'sqlite3'
|
27
|
+
spec.add_development_dependency "awesome_print"
|
28
|
+
spec.add_development_dependency "pry"
|
29
|
+
spec.add_development_dependency "pry-nav"
|
30
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
belongs_to :company
|
3
|
+
belongs_to :family
|
4
|
+
|
5
|
+
has_one :address, dependent: :destroy
|
6
|
+
|
7
|
+
has_many :dependent_users, dependent: :destroy
|
8
|
+
has_many :tasks, dependent: :destroy
|
9
|
+
|
10
|
+
#has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
|
11
|
+
#validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
|
12
|
+
end
|
13
|
+
|
14
|
+
class Address < ActiveRecord::Base
|
15
|
+
end
|
16
|
+
|
17
|
+
class Company < ActiveRecord::Base
|
18
|
+
has_many :users
|
19
|
+
end
|
20
|
+
|
21
|
+
class DependentUser < ActiveRecord::Base
|
22
|
+
belongs_to :user
|
23
|
+
end
|
24
|
+
|
25
|
+
class Family < ActiveRecord::Base
|
26
|
+
end
|
27
|
+
|
28
|
+
class Task < ActiveRecord::Base
|
29
|
+
attr_accessor :error_message
|
30
|
+
end
|
31
|
+
|
32
|
+
#migrations
|
33
|
+
class CreateAllTables < ActiveRecord::Migration
|
34
|
+
def self.up
|
35
|
+
create_table :users do |t|
|
36
|
+
t.string :name
|
37
|
+
t.references :company, index: true
|
38
|
+
t.references :family, index: true
|
39
|
+
|
40
|
+
t.timestamps
|
41
|
+
end
|
42
|
+
|
43
|
+
create_table :companies do |t|
|
44
|
+
t.string :name
|
45
|
+
t.string :website
|
46
|
+
|
47
|
+
t.timestamps
|
48
|
+
end
|
49
|
+
|
50
|
+
create_table :dependent_users do |t|
|
51
|
+
t.string :name
|
52
|
+
t.date :date_birth
|
53
|
+
t.references :user, index: true
|
54
|
+
|
55
|
+
t.timestamps
|
56
|
+
end
|
57
|
+
|
58
|
+
create_table :tasks do |t|
|
59
|
+
t.string :name
|
60
|
+
t.references :user, index: true
|
61
|
+
|
62
|
+
t.timestamps
|
63
|
+
end
|
64
|
+
|
65
|
+
create_table :families do |t|
|
66
|
+
t.string :last_name
|
67
|
+
|
68
|
+
t.timestamps
|
69
|
+
end
|
70
|
+
|
71
|
+
create_table :addresses do |t|
|
72
|
+
t.string :street
|
73
|
+
t.string :district
|
74
|
+
|
75
|
+
t.references :user, index: true
|
76
|
+
|
77
|
+
t.timestamps
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
ActiveRecord::Migration.verbose = false
|
82
|
+
CreateAllTables.up
|
83
|
+
|
84
|
+
# seed
|
85
|
+
bar = User.create(name: 'Bar')
|
86
|
+
bar.tasks << Task.create(name: 'Task 1')
|
87
|
+
bar.dependent_users << DependentUser.create(name: 'John', date_birth: Date.new(1990, 2, 1))
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#require 'action_controller/railtie'
|
2
|
+
#require 'action_view/railtie'
|
3
|
+
require 'active_record/railtie'
|
4
|
+
require 'active_model/railtie'
|
5
|
+
|
6
|
+
# config
|
7
|
+
app = Class.new(Rails::Application)
|
8
|
+
app.config.secret_token = '3b7cd7445w4d5w4d5w4w5566c4'
|
9
|
+
app.config.session_store :cookie_store, :key => '_myapp_session'
|
10
|
+
app.config.active_support.deprecation = :log
|
11
|
+
app.config.eager_load = false
|
12
|
+
# Rais.root
|
13
|
+
app.config.root = File.dirname(__FILE__)
|
14
|
+
Rails.backtrace_cleaner.remove_silencers!
|
15
|
+
app.initialize!
|
16
|
+
|
17
|
+
require_relative 'active_record/models'
|
18
|
+
require_relative 'users_form'
|
19
|
+
|
20
|
+
# controllers
|
21
|
+
# class ApplicationController < ActionController::Base; end
|
22
|
+
|
23
|
+
# class UsersController < ApplicationController
|
24
|
+
# def new
|
25
|
+
# @users_form = UserForm.new(User.new)
|
26
|
+
# end
|
27
|
+
# end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe UsersForm do
|
4
|
+
let(:users_form) { UsersForm.new(User.new) }
|
5
|
+
let(:users_form_existing_user) { UsersForm.new(User.find(1)) }
|
6
|
+
|
7
|
+
it { expect(UsersForm._main_model).to eq('User') }
|
8
|
+
|
9
|
+
subject(:users_form_to_model) { users_form.to_model }
|
10
|
+
it { expect(users_form_to_model.class).to eq(User) }
|
11
|
+
it { expect(users_form_to_model.persisted?).to eq(false) }
|
12
|
+
|
13
|
+
it { expect(UsersForm.ancestors).to include(Linker) }
|
14
|
+
it { expect(UsersForm.ancestors).to include(ActiveModel::Validations) } if Rails.version.to_i < 4
|
15
|
+
it { expect(UsersForm.ancestors).to include(ActiveModel::Model) } if Rails.version.to_i >= 4
|
16
|
+
|
17
|
+
it { expect(users_form).to respond_to(:user) }
|
18
|
+
subject(:user) { users_form.user }
|
19
|
+
it { expect(user).to be_an(User) }
|
20
|
+
|
21
|
+
it { expect(users_form).to respond_to(:params=) }
|
22
|
+
it { expect(users_form).to respond_to(:save) }
|
23
|
+
|
24
|
+
it { expect(users_form).to respond_to(:dependent_users, :dependent_users_attributes=) }
|
25
|
+
it { expect(users_form.dependent_users).to be_an(Array) }
|
26
|
+
|
27
|
+
subject(:dependent_users_sample) { users_form.dependent_users.sample }
|
28
|
+
it { expect(dependent_users_sample).to be_an(DependentUser) }
|
29
|
+
|
30
|
+
it { expect(users_form).to respond_to(:tasks, :tasks_attributes=) }
|
31
|
+
it { expect(users_form.tasks).to be_an(Array) }
|
32
|
+
subject(:tasks_sample) { users_form.tasks.sample }
|
33
|
+
it { expect(tasks_sample).to be_an(Task) }
|
34
|
+
|
35
|
+
it { expect(users_form).to respond_to(:address, :address_attributes=) }
|
36
|
+
subject(:address) { users_form.address }
|
37
|
+
it { expect(address).to be_an(Address) }
|
38
|
+
|
39
|
+
it { expect(users_form).to respond_to(:company, :company_attributes=) }
|
40
|
+
subject(:company) { users_form.company }
|
41
|
+
it { expect(company).to be_an(Company) }
|
42
|
+
|
43
|
+
it { expect(users_form).to respond_to(:family, :family_attributes=) }
|
44
|
+
subject(:family) { users_form.family }
|
45
|
+
it { expect(family).to be_an(Family) }
|
46
|
+
|
47
|
+
context 'new' do
|
48
|
+
it { expect(user.persisted?).to eq(false) }
|
49
|
+
|
50
|
+
it { expect(users_form.save).to eq(false) }
|
51
|
+
it { expect(users_form).not_to be_valid }
|
52
|
+
it do
|
53
|
+
users_form.save
|
54
|
+
expect(users_form.errors.full_messages).to include("Name can't be blank")
|
55
|
+
end
|
56
|
+
|
57
|
+
it { expect(dependent_users_sample.persisted?).to eq(false) }
|
58
|
+
|
59
|
+
it { expect(tasks_sample.persisted?).to eq(false) }
|
60
|
+
|
61
|
+
it { expect(address.persisted?).to eq(false) }
|
62
|
+
|
63
|
+
it { expect(company.persisted?).to eq(false) }
|
64
|
+
|
65
|
+
it { expect(family.persisted?).to eq(false) }
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'create' do
|
69
|
+
before do
|
70
|
+
users_form.params = {
|
71
|
+
'name' => "Foo",
|
72
|
+
'company_attributes' => {'id' => '', 'name' => 'My Company', 'website' => 'mycompany.com'},
|
73
|
+
'family_attributes' => {'id' => '', 'last_name' => 'Milan'},
|
74
|
+
'address_attributes' => {'id' => '', 'street' => '', 'district' => ''},
|
75
|
+
'tasks_attributes' => {'0' => {'id' => '', 'name' => 'T1'}, '1' => {'id' => '', 'name' => 'T2'}},
|
76
|
+
'dependent_users_attributes' => {'0' => {'id' => '', 'name' => '', 'date_birth' => ''}, '1' => {'id' => '', 'name' => '', 'date_birth' => ''}}
|
77
|
+
}
|
78
|
+
users_form.save
|
79
|
+
end
|
80
|
+
|
81
|
+
it { expect(users_form).to be_valid }
|
82
|
+
it { expect(user.persisted?).to eq(true) }
|
83
|
+
it { expect(user.name).to eq('Foo') }
|
84
|
+
|
85
|
+
it { expect(tasks_sample.persisted?).to eq(true) }
|
86
|
+
it { expect(users_form.tasks.first.id).to eq(user.tasks.first.id) }
|
87
|
+
it { expect(user.tasks.first.name).to eq('T1') }
|
88
|
+
it { expect(user.tasks.last.name).to eq('T2') }
|
89
|
+
|
90
|
+
it { expect(dependent_users_sample.persisted?).to eq(true) }
|
91
|
+
it { expect(dependent_users_sample.name).to eq('') }
|
92
|
+
|
93
|
+
subject(:user_company) { user.company }
|
94
|
+
it { expect(user_company.persisted?).to eq(true) }
|
95
|
+
it { expect(users_form.company.id).to eq(user_company.id) }
|
96
|
+
it { expect(user_company.name).to eq('My Company') }
|
97
|
+
it { expect(user_company.website).to eq('mycompany.com') }
|
98
|
+
|
99
|
+
subject(:user_family) { user.family }
|
100
|
+
it { expect(user_family.persisted?).to eq(true) }
|
101
|
+
it { expect(users_form.family.id).to eq(user_family.id) }
|
102
|
+
it { expect(user_family.last_name).to eq('Milan') }
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'update' do
|
106
|
+
before do
|
107
|
+
users_form_existing_user.params = {
|
108
|
+
'name' => "Bar",
|
109
|
+
'company_attributes' => {'id' => '', 'name' => 'My Company', 'website' => 'mycompany.com'},
|
110
|
+
'family_attributes' => {'id' => '', 'last_name' => 'Milan'},
|
111
|
+
'address_attributes' => {'id' => '', 'street' => '', 'district' => ''},
|
112
|
+
#'tasks_attributes' => {'0' => {'id' => '', 'name' => 'T1'}, '1' => {'id' => '', 'name' => 'T2'}},
|
113
|
+
'dependent_users_attributes' => {'0' => {'id' => '1', 'name' => 'John 2', 'date_birth' => Date.new(1990, 2, 2)}, '1' => {'id' => '', 'name' => '', 'date_birth' => ''}}
|
114
|
+
}
|
115
|
+
users_form_existing_user.save
|
116
|
+
end
|
117
|
+
|
118
|
+
subject(:users_form_existing_user_user) { users_form_existing_user.user }
|
119
|
+
subject(:users_form_existing_user_dependent_users) { users_form_existing_user.dependent_users }
|
120
|
+
subject(:users_form_existing_user_user_dependent_users) { users_form_existing_user.user.dependent_users }
|
121
|
+
subject(:users_form_existing_user_company) { users_form_existing_user.company }
|
122
|
+
subject(:users_form_existing_user_address) { users_form_existing_user.address }
|
123
|
+
subject(:users_form_existing_user_family) { users_form_existing_user.family }
|
124
|
+
subject(:users_form_existing_user_user_tasks) { users_form_existing_user.user.tasks }
|
125
|
+
subject(:users_form_existing_user_tasks_sample) { users_form_existing_user.tasks.sample }
|
126
|
+
|
127
|
+
subject(:users_form_existing_user_to_model) { users_form_existing_user.to_model }
|
128
|
+
it { expect(users_form_existing_user_to_model.class).to eq(User) }
|
129
|
+
it { expect(users_form_existing_user_to_model.persisted?).to eq(true) }
|
130
|
+
|
131
|
+
it { expect(users_form_existing_user_user_tasks.empty?).to be(true) }
|
132
|
+
it { expect(users_form_existing_user_tasks_sample.persisted?).to be(false) }
|
133
|
+
it { expect(users_form_existing_user_tasks_sample).to be_a(Task) }
|
134
|
+
|
135
|
+
it { expect(users_form_existing_user_dependent_users.sample.persisted?).to eq(true) }
|
136
|
+
it { expect(users_form_existing_user_dependent_users.first.id).to eq(1) }
|
137
|
+
it { expect(users_form_existing_user_dependent_users.first.name).to eq('John 2') }
|
138
|
+
|
139
|
+
it { expect(users_form_existing_user_user_dependent_users.sample.persisted?).to eq(true)}
|
140
|
+
it { expect(users_form_existing_user_user_dependent_users.first.id).to eq(1)}
|
141
|
+
it { expect(users_form_existing_user_user_dependent_users.first.name).to eq('John 2')}
|
142
|
+
|
143
|
+
it { expect(users_form_existing_user_user.persisted?).to eq(true)}
|
144
|
+
it { expect(users_form_existing_user_user.name).to eq('Bar')}
|
145
|
+
|
146
|
+
it { expect(users_form_existing_user_company.persisted?).to be(true) }
|
147
|
+
it { expect(users_form_existing_user_company.name).to eq('My Company') }
|
148
|
+
it { expect(users_form_existing_user_company.website).to eq('mycompany.com') }
|
149
|
+
|
150
|
+
it { expect(users_form_existing_user_family.persisted?).to be(true) }
|
151
|
+
it { expect(users_form_existing_user_family.last_name).to eq('Milan') }
|
152
|
+
|
153
|
+
it { expect(users_form_existing_user_address.persisted?).to be(true) }
|
154
|
+
it { expect(users_form_existing_user_address.street).to eq('') }
|
155
|
+
end
|
156
|
+
end
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: linker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Glauco Custódio
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: awesome_print
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: pry-nav
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: A wrapper to form objects in ActiveRecord. Forget accepts_nested_attributes_for.
|
126
|
+
email:
|
127
|
+
- glauco.custodio@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- ".rspec"
|
134
|
+
- ".travis.yml"
|
135
|
+
- Gemfile
|
136
|
+
- LICENSE
|
137
|
+
- README.md
|
138
|
+
- Rakefile
|
139
|
+
- lib/linker.rb
|
140
|
+
- lib/linker/forms/attributes.rb
|
141
|
+
- lib/linker/forms/configuration_methods.rb
|
142
|
+
- lib/linker/forms/params.rb
|
143
|
+
- lib/linker/version.rb
|
144
|
+
- linker.gemspec
|
145
|
+
- spec/fake_app/active_record/models.rb
|
146
|
+
- spec/fake_app/config/database.yml
|
147
|
+
- spec/fake_app/rails_app.rb
|
148
|
+
- spec/fake_app/users_form.rb
|
149
|
+
- spec/spec_helper.rb
|
150
|
+
- spec/users_form_spec.rb
|
151
|
+
homepage: https://github.com/glaucocustodio/linker
|
152
|
+
licenses:
|
153
|
+
- MIT
|
154
|
+
metadata: {}
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubyforge_project:
|
171
|
+
rubygems_version: 2.4.1
|
172
|
+
signing_key:
|
173
|
+
specification_version: 4
|
174
|
+
summary: A wrapper to form objects in ActiveRecord. Forget accepts_nested_attributes_for.
|
175
|
+
test_files:
|
176
|
+
- spec/fake_app/active_record/models.rb
|
177
|
+
- spec/fake_app/config/database.yml
|
178
|
+
- spec/fake_app/rails_app.rb
|
179
|
+
- spec/fake_app/users_form.rb
|
180
|
+
- spec/spec_helper.rb
|
181
|
+
- spec/users_form_spec.rb
|