active_model_serializers_rails_2.3 0.9.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +87 -0
- data/CONTRIBUTING.md +20 -0
- data/DESIGN.textile +586 -0
- data/MIT-LICENSE +21 -0
- data/README.md +793 -0
- data/lib/active_model/array_serializer.rb +60 -0
- data/lib/active_model/default_serializer.rb +27 -0
- data/lib/active_model/serializable.rb +25 -0
- data/lib/active_model/serializer.rb +220 -0
- data/lib/active_model/serializer/associations.rb +98 -0
- data/lib/active_model/serializer/config.rb +31 -0
- data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +14 -0
- data/lib/active_model/serializer/generators/serializer/templates/controller.rb +93 -0
- data/lib/active_model/serializer/railtie.rb +10 -0
- data/lib/active_model/serializer/version.rb +5 -0
- data/lib/active_model/serializer_support.rb +5 -0
- data/lib/active_model_serializers.rb +33 -0
- data/test/coverage_setup.rb +15 -0
- data/test/fixtures/active_record.rb +92 -0
- data/test/fixtures/poro.rb +64 -0
- data/test/integration/active_record/active_record_test.rb +77 -0
- data/test/test_app.rb +11 -0
- data/test/test_helper.rb +13 -0
- data/test/unit/active_model/array_serializer/meta_test.rb +53 -0
- data/test/unit/active_model/array_serializer/root_test.rb +102 -0
- data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
- data/test/unit/active_model/array_serializer/serialization_test.rb +182 -0
- data/test/unit/active_model/default_serializer_test.rb +13 -0
- data/test/unit/active_model/serializer/associations/build_serializer_test.rb +21 -0
- data/test/unit/active_model/serializer/associations_test.rb +19 -0
- data/test/unit/active_model/serializer/attributes_test.rb +41 -0
- data/test/unit/active_model/serializer/config_test.rb +86 -0
- data/test/unit/active_model/serializer/filter_test.rb +49 -0
- data/test/unit/active_model/serializer/has_many_test.rb +174 -0
- data/test/unit/active_model/serializer/has_one_test.rb +151 -0
- data/test/unit/active_model/serializer/meta_test.rb +39 -0
- data/test/unit/active_model/serializer/root_test.rb +117 -0
- data/test/unit/active_model/serializer/scope_test.rb +49 -0
- metadata +127 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
|
3
|
+
|
4
|
+
module Rails
|
5
|
+
module Generators
|
6
|
+
class ScaffoldControllerGenerator
|
7
|
+
if Rails::VERSION::MAJOR >= 4
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
|
10
|
+
hook_for :serializer, default: true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
<% if namespaced? -%>
|
2
|
+
require_dependency "<%= namespaced_file_path %>/application_controller"
|
3
|
+
|
4
|
+
<% end -%>
|
5
|
+
<% module_namespacing do -%>
|
6
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
7
|
+
before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
|
8
|
+
|
9
|
+
# GET <%= route_url %>
|
10
|
+
# GET <%= route_url %>.json
|
11
|
+
def index
|
12
|
+
@<%= plural_table_name %> = <%= orm_class.all(class_name) %>
|
13
|
+
|
14
|
+
respond_to do |format|
|
15
|
+
format.html # index.html.erb
|
16
|
+
format.json { render json: <%= "@#{plural_table_name}" %> }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# GET <%= route_url %>/1
|
21
|
+
# GET <%= route_url %>/1.json
|
22
|
+
def show
|
23
|
+
respond_to do |format|
|
24
|
+
format.html # show.html.erb
|
25
|
+
format.json { render json: <%= "@#{singular_table_name}" %> }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# GET <%= route_url %>/new
|
30
|
+
def new
|
31
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name) %>
|
32
|
+
end
|
33
|
+
|
34
|
+
# GET <%= route_url %>/1/edit
|
35
|
+
def edit
|
36
|
+
end
|
37
|
+
|
38
|
+
# POST <%= route_url %>
|
39
|
+
# POST <%= route_url %>.json
|
40
|
+
def create
|
41
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
|
42
|
+
|
43
|
+
respond_to do |format|
|
44
|
+
if @<%= orm_instance.save %>
|
45
|
+
format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> }
|
46
|
+
format.json { render json: <%= "@#{singular_table_name}" %>, status: :created }
|
47
|
+
else
|
48
|
+
format.html { render action: 'new' }
|
49
|
+
format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# PATCH/PUT <%= route_url %>/1
|
55
|
+
# PATCH/PUT <%= route_url %>/1.json
|
56
|
+
def update
|
57
|
+
respond_to do |format|
|
58
|
+
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
59
|
+
format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> }
|
60
|
+
format.json { head :no_content }
|
61
|
+
else
|
62
|
+
format.html { render action: 'edit' }
|
63
|
+
format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# DELETE <%= route_url %>/1
|
69
|
+
# DELETE <%= route_url %>/1.json
|
70
|
+
def destroy
|
71
|
+
@<%= orm_instance.destroy %>
|
72
|
+
respond_to do |format|
|
73
|
+
format.html { redirect_to <%= index_helper %>_url }
|
74
|
+
format.json { head :no_content }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
# Use callbacks to share common setup or constraints between actions.
|
80
|
+
def set_<%= singular_table_name %>
|
81
|
+
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
|
82
|
+
end
|
83
|
+
|
84
|
+
# Never trust parameters from the scary internet, only allow the white list through.
|
85
|
+
def <%= "#{singular_table_name}_params" %>
|
86
|
+
<%- if attributes_names.empty? -%>
|
87
|
+
params[<%= ":#{singular_table_name}" %>]
|
88
|
+
<%- else -%>
|
89
|
+
params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
|
90
|
+
<%- end -%>
|
91
|
+
end
|
92
|
+
end
|
93
|
+
<% end -%>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
initializer 'generators' do |app|
|
4
|
+
app.load_generators
|
5
|
+
require 'active_model/serializer/generators/serializer/serializer_generator'
|
6
|
+
require 'active_model/serializer/generators/serializer/scaffold_controller_generator'
|
7
|
+
require 'active_model/serializer/generators/resource_override'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_model/serializer'
|
4
|
+
require 'active_model/serializer_support'
|
5
|
+
require 'active_model/serializer/version'
|
6
|
+
require 'active_model/serializer/railtie' if defined?(Rails) && defined?(Rails::Railtie)
|
7
|
+
|
8
|
+
ActiveModel::Errors = ActiveRecord::Errors #fix for authlogic gem
|
9
|
+
|
10
|
+
module ActiveSupport
|
11
|
+
module Inflector
|
12
|
+
def safe_constantize(camel_cased_word)
|
13
|
+
begin
|
14
|
+
constantize(camel_cased_word)
|
15
|
+
rescue NameError => e
|
16
|
+
raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
|
17
|
+
e.name.to_s == camel_cased_word.to_s
|
18
|
+
rescue ArgumentError => e
|
19
|
+
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
begin
|
25
|
+
require 'action_controller'
|
26
|
+
require 'action_controller/serialization'
|
27
|
+
|
28
|
+
ActiveSupport.on_load(:action_controller) do
|
29
|
+
include ::ActionController::Serialization
|
30
|
+
end
|
31
|
+
rescue LoadError
|
32
|
+
# rails not installed, continuing
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
begin
|
2
|
+
require 'simplecov'
|
3
|
+
require 'coveralls'
|
4
|
+
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
6
|
+
SimpleCov::Formatter::HTMLFormatter,
|
7
|
+
Coveralls::SimpleCov::Formatter
|
8
|
+
]
|
9
|
+
|
10
|
+
SimpleCov.start do
|
11
|
+
add_group "lib", "lib"
|
12
|
+
add_group "test", "test"
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
:adapter => 'sqlite3',
|
5
|
+
:database => ':memory:'
|
6
|
+
)
|
7
|
+
|
8
|
+
ActiveRecord::Schema.define do
|
9
|
+
create_table :ar_posts, force: true do |t|
|
10
|
+
t.string :title
|
11
|
+
t.text :body
|
12
|
+
t.belongs_to :ar_section, index: true
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :ar_comments, force: true do |t|
|
17
|
+
t.text :body
|
18
|
+
t.belongs_to :ar_post, index: true
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :ar_tags, force: true do |t|
|
23
|
+
t.string :name
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table :ar_sections, force: true do |t|
|
27
|
+
t.string :name
|
28
|
+
end
|
29
|
+
|
30
|
+
create_table :ar_posts_tags, force: true, id: false do |t|
|
31
|
+
t.references :ar_post, :ar_tag, index: true
|
32
|
+
end
|
33
|
+
|
34
|
+
create_table :ar_comments_tags, force: true, id: false do |t|
|
35
|
+
t.references :ar_comment, :ar_tag, index: true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ARPost < ActiveRecord::Base
|
40
|
+
has_many :ar_comments, class_name: 'ARComment'
|
41
|
+
has_and_belongs_to_many :ar_tags, class_name: 'ARTag', join_table: :ar_posts_tags
|
42
|
+
belongs_to :ar_section, class_name: 'ARSection'
|
43
|
+
end
|
44
|
+
|
45
|
+
class ARComment < ActiveRecord::Base
|
46
|
+
belongs_to :ar_post, class_name: 'ARPost'
|
47
|
+
has_and_belongs_to_many :ar_tags, class_name: 'ARTag', join_table: :ar_comments_tags
|
48
|
+
end
|
49
|
+
|
50
|
+
class ARTag < ActiveRecord::Base
|
51
|
+
end
|
52
|
+
|
53
|
+
class ARSection < ActiveRecord::Base
|
54
|
+
end
|
55
|
+
|
56
|
+
class ARPostSerializer < ActiveModel::Serializer
|
57
|
+
attributes :title, :body
|
58
|
+
|
59
|
+
has_many :ar_comments, :ar_tags
|
60
|
+
has_one :ar_section
|
61
|
+
end
|
62
|
+
|
63
|
+
class ARCommentSerializer < ActiveModel::Serializer
|
64
|
+
attributes :body
|
65
|
+
|
66
|
+
has_many :ar_tags
|
67
|
+
end
|
68
|
+
|
69
|
+
class ARTagSerializer < ActiveModel::Serializer
|
70
|
+
attributes :name
|
71
|
+
end
|
72
|
+
|
73
|
+
class ARSectionSerializer < ActiveModel::Serializer
|
74
|
+
attributes 'name'
|
75
|
+
end
|
76
|
+
|
77
|
+
ARPost.create(title: 'New post',
|
78
|
+
body: 'A body!!!',
|
79
|
+
ar_section: ARSection.create(name: 'ruby')).tap do |post|
|
80
|
+
|
81
|
+
short_tag = post.ar_tags.create(name: 'short')
|
82
|
+
whiny_tag = post.ar_tags.create(name: 'whiny')
|
83
|
+
happy_tag = ARTag.create(name: 'happy')
|
84
|
+
|
85
|
+
post.ar_comments.create(body: 'what a dumb post').tap do |comment|
|
86
|
+
comment.ar_tags.concat happy_tag, whiny_tag
|
87
|
+
end
|
88
|
+
|
89
|
+
post.ar_comments.create(body: 'i liked it').tap do |comment|
|
90
|
+
comment.ar_tags.concat happy_tag, short_tag
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class Model
|
2
|
+
def initialize(hash={})
|
3
|
+
@attributes = hash
|
4
|
+
end
|
5
|
+
|
6
|
+
def read_attribute_for_serialization(name)
|
7
|
+
if name == :id || name == 'id'
|
8
|
+
object_id
|
9
|
+
else
|
10
|
+
@attributes[name]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
###
|
17
|
+
## Models
|
18
|
+
###
|
19
|
+
class User < Model
|
20
|
+
def profile
|
21
|
+
@profile ||= Profile.new(name: 'N1', description: 'D1')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Profile < Model
|
26
|
+
end
|
27
|
+
|
28
|
+
class Post < Model
|
29
|
+
def comments
|
30
|
+
@comments ||= [Comment.new(content: 'C1'),
|
31
|
+
Comment.new(content: 'C2')]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Comment < Model
|
36
|
+
end
|
37
|
+
|
38
|
+
###
|
39
|
+
## Serializers
|
40
|
+
###
|
41
|
+
class UserSerializer < ActiveModel::Serializer
|
42
|
+
attributes :name, :email
|
43
|
+
|
44
|
+
has_one :profile
|
45
|
+
end
|
46
|
+
|
47
|
+
class ProfileSerializer < ActiveModel::Serializer
|
48
|
+
def description
|
49
|
+
description = object.read_attribute_for_serialization(:description)
|
50
|
+
scope ? "#{description} - #{scope}" : description
|
51
|
+
end
|
52
|
+
|
53
|
+
attributes :name, :description
|
54
|
+
end
|
55
|
+
|
56
|
+
class PostSerializer < ActiveModel::Serializer
|
57
|
+
attributes :title, :body
|
58
|
+
|
59
|
+
has_many :comments
|
60
|
+
end
|
61
|
+
|
62
|
+
class CommentSerializer < ActiveModel::Serializer
|
63
|
+
attributes :content
|
64
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'fixtures/active_record'
|
3
|
+
|
4
|
+
module ActiveModel
|
5
|
+
class Serializer
|
6
|
+
class ActiveRecordTest < Minitest::Test
|
7
|
+
def setup
|
8
|
+
@post = ARPost.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_serialization_embedding_objects
|
12
|
+
post_serializer = ARPostSerializer.new(@post)
|
13
|
+
|
14
|
+
assert_equal({
|
15
|
+
'ar_post' => {
|
16
|
+
title: 'New post', body: 'A body!!!',
|
17
|
+
ar_comments: [{ body: 'what a dumb post', ar_tags: [{ name: 'happy' }, { name: 'whiny' }] },
|
18
|
+
{ body: 'i liked it', ar_tags: [{:name=>"happy"}, {:name=>"short"}] }],
|
19
|
+
ar_tags: [{ name: 'short' }, { name: 'whiny' }],
|
20
|
+
ar_section: { 'name' => 'ruby' }
|
21
|
+
}
|
22
|
+
}, post_serializer.as_json)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_serialization_embedding_ids
|
26
|
+
post_serializer = ARPostSerializer.new(@post)
|
27
|
+
|
28
|
+
embed(ARPostSerializer, embed: :ids) do
|
29
|
+
assert_equal({
|
30
|
+
'ar_post' => {
|
31
|
+
title: 'New post', body: 'A body!!!',
|
32
|
+
'ar_comment_ids' => [1, 2],
|
33
|
+
'ar_tag_ids' => [1, 2],
|
34
|
+
'ar_section_id' => 1
|
35
|
+
}
|
36
|
+
}, post_serializer.as_json)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_serialization_embedding_ids_including_in_root
|
41
|
+
post_serializer = ARPostSerializer.new(@post)
|
42
|
+
|
43
|
+
embed(ARPostSerializer, embed: :ids, embed_in_root: true) do
|
44
|
+
embed(ARCommentSerializer, embed: :ids, embed_in_root: true) do
|
45
|
+
assert_equal({
|
46
|
+
'ar_post' => {
|
47
|
+
title: 'New post', body: 'A body!!!',
|
48
|
+
'ar_comment_ids' => [1, 2],
|
49
|
+
'ar_tag_ids' => [1, 2],
|
50
|
+
'ar_section_id' => 1
|
51
|
+
},
|
52
|
+
ar_comments: [{ body: 'what a dumb post', 'ar_tag_ids' => [3, 2] },
|
53
|
+
{ body: 'i liked it', 'ar_tag_ids' => [3, 1] }],
|
54
|
+
ar_tags: [{ name: 'happy' }, { name: 'whiny' }, { name: 'short' }],
|
55
|
+
'ar_sections' => [{ 'name' => 'ruby' }]
|
56
|
+
}, post_serializer.as_json)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def embed(serializer_class, options = {})
|
64
|
+
old_assocs = Hash[serializer_class._associations.to_a.map { |(name, association)| [name, association.dup] }]
|
65
|
+
|
66
|
+
serializer_class._associations.each_value do |association|
|
67
|
+
association.embed = options[:embed]
|
68
|
+
association.embed_in_root = options[:embed_in_root]
|
69
|
+
end
|
70
|
+
|
71
|
+
yield
|
72
|
+
ensure
|
73
|
+
serializer_class._associations = old_assocs
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/test/test_app.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
class TestApp < Rails::Application
|
2
|
+
if Rails.version.to_s.first >= '4'
|
3
|
+
config.eager_load = false
|
4
|
+
config.secret_key_base = 'abc123'
|
5
|
+
end
|
6
|
+
|
7
|
+
# Set up a logger to avoid creating a log directory on every run.
|
8
|
+
config.logger = Logger.new(nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
TestApp.initialize!
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'coverage_setup'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'active_model_serializers'
|
5
|
+
require 'fixtures/poro'
|
6
|
+
require 'fixtures/active_record'
|
7
|
+
|
8
|
+
# Ensure backward compatibility with Minitest 4
|
9
|
+
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
|
10
|
+
|
11
|
+
module TestHelper
|
12
|
+
end
|
13
|
+
|