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.
Files changed (39) hide show
  1. data/CHANGELOG.md +87 -0
  2. data/CONTRIBUTING.md +20 -0
  3. data/DESIGN.textile +586 -0
  4. data/MIT-LICENSE +21 -0
  5. data/README.md +793 -0
  6. data/lib/active_model/array_serializer.rb +60 -0
  7. data/lib/active_model/default_serializer.rb +27 -0
  8. data/lib/active_model/serializable.rb +25 -0
  9. data/lib/active_model/serializer.rb +220 -0
  10. data/lib/active_model/serializer/associations.rb +98 -0
  11. data/lib/active_model/serializer/config.rb +31 -0
  12. data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +14 -0
  13. data/lib/active_model/serializer/generators/serializer/templates/controller.rb +93 -0
  14. data/lib/active_model/serializer/railtie.rb +10 -0
  15. data/lib/active_model/serializer/version.rb +5 -0
  16. data/lib/active_model/serializer_support.rb +5 -0
  17. data/lib/active_model_serializers.rb +33 -0
  18. data/test/coverage_setup.rb +15 -0
  19. data/test/fixtures/active_record.rb +92 -0
  20. data/test/fixtures/poro.rb +64 -0
  21. data/test/integration/active_record/active_record_test.rb +77 -0
  22. data/test/test_app.rb +11 -0
  23. data/test/test_helper.rb +13 -0
  24. data/test/unit/active_model/array_serializer/meta_test.rb +53 -0
  25. data/test/unit/active_model/array_serializer/root_test.rb +102 -0
  26. data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
  27. data/test/unit/active_model/array_serializer/serialization_test.rb +182 -0
  28. data/test/unit/active_model/default_serializer_test.rb +13 -0
  29. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +21 -0
  30. data/test/unit/active_model/serializer/associations_test.rb +19 -0
  31. data/test/unit/active_model/serializer/attributes_test.rb +41 -0
  32. data/test/unit/active_model/serializer/config_test.rb +86 -0
  33. data/test/unit/active_model/serializer/filter_test.rb +49 -0
  34. data/test/unit/active_model/serializer/has_many_test.rb +174 -0
  35. data/test/unit/active_model/serializer/has_one_test.rb +151 -0
  36. data/test/unit/active_model/serializer/meta_test.rb +39 -0
  37. data/test/unit/active_model/serializer/root_test.rb +117 -0
  38. data/test/unit/active_model/serializer/scope_test.rb +49 -0
  39. 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,5 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ VERSION = '0.9.0.alpha1'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveModel
2
+ module SerializerSupport
3
+ alias read_attribute_for_serialization send
4
+ end
5
+ 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
@@ -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!
@@ -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
+