active_model_serializers_rails_2.3 0.9.0.alpha1

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 (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
+