active_model_serializers 0.8.4 → 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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -45
  3. data/CONTRIBUTING.md +20 -0
  4. data/DESIGN.textile +4 -4
  5. data/{MIT-LICENSE.txt → MIT-LICENSE} +0 -0
  6. data/README.md +187 -113
  7. data/lib/action_controller/serialization.rb +30 -16
  8. data/lib/active_model/array_serializer.rb +36 -82
  9. data/lib/active_model/default_serializer.rb +22 -0
  10. data/lib/active_model/serializable.rb +25 -0
  11. data/lib/active_model/serializer.rb +126 -447
  12. data/lib/active_model/serializer/associations.rb +53 -211
  13. data/lib/active_model/serializer/config.rb +31 -0
  14. data/lib/active_model/serializer/generators/resource_override.rb +13 -0
  15. data/lib/{generators → active_model/serializer/generators}/serializer/USAGE +0 -0
  16. data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +14 -0
  17. data/lib/active_model/serializer/generators/serializer/serializer_generator.rb +37 -0
  18. data/lib/active_model/serializer/generators/serializer/templates/controller.rb +93 -0
  19. data/lib/active_model/serializer/generators/serializer/templates/serializer.rb +8 -0
  20. data/lib/active_model/serializer/railtie.rb +10 -0
  21. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  22. data/lib/active_model/serializer_support.rb +5 -0
  23. data/lib/active_model_serializers.rb +7 -86
  24. data/test/coverage_setup.rb +15 -0
  25. data/test/fixtures/active_record.rb +92 -0
  26. data/test/fixtures/poro.rb +64 -0
  27. data/test/integration/action_controller/serialization_test.rb +234 -0
  28. data/test/integration/active_record/active_record_test.rb +77 -0
  29. data/test/integration/generators/resource_generator_test.rb +26 -0
  30. data/test/integration/generators/scaffold_controller_generator_test.rb +67 -0
  31. data/test/integration/generators/serializer_generator_test.rb +41 -0
  32. data/test/test_app.rb +11 -0
  33. data/test/test_helper.rb +7 -41
  34. data/test/tmp/app/serializers/account_serializer.rb +3 -0
  35. data/test/unit/active_model/array_serializer/meta_test.rb +53 -0
  36. data/test/unit/active_model/array_serializer/root_test.rb +102 -0
  37. data/test/unit/active_model/array_serializer/scope_test.rb +24 -0
  38. data/test/unit/active_model/array_serializer/serialization_test.rb +83 -0
  39. data/test/unit/active_model/default_serializer_test.rb +13 -0
  40. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +21 -0
  41. data/test/unit/active_model/serializer/associations_test.rb +19 -0
  42. data/test/unit/active_model/serializer/attributes_test.rb +41 -0
  43. data/test/unit/active_model/serializer/config_test.rb +86 -0
  44. data/test/unit/active_model/serializer/filter_test.rb +49 -0
  45. data/test/unit/active_model/serializer/has_many_test.rb +173 -0
  46. data/test/unit/active_model/serializer/has_one_test.rb +151 -0
  47. data/test/unit/active_model/serializer/meta_test.rb +39 -0
  48. data/test/unit/active_model/serializer/root_test.rb +117 -0
  49. data/test/unit/active_model/serializer/scope_test.rb +49 -0
  50. metadata +78 -74
  51. data/.gitignore +0 -18
  52. data/.travis.yml +0 -34
  53. data/Gemfile +0 -38
  54. data/Rakefile +0 -22
  55. data/active_model_serializers.gemspec +0 -24
  56. data/appveyor.yml +0 -27
  57. data/bench/perf.rb +0 -43
  58. data/cruft.md +0 -19
  59. data/lib/active_record/serializer_override.rb +0 -16
  60. data/lib/generators/resource_override.rb +0 -13
  61. data/lib/generators/serializer/serializer_generator.rb +0 -42
  62. data/lib/generators/serializer/templates/serializer.rb +0 -19
  63. data/test/array_serializer_test.rb +0 -75
  64. data/test/association_test.rb +0 -592
  65. data/test/caching_test.rb +0 -177
  66. data/test/generators_test.rb +0 -85
  67. data/test/no_serialization_scope_test.rb +0 -34
  68. data/test/serialization_scope_name_test.rb +0 -67
  69. data/test/serialization_test.rb +0 -396
  70. data/test/serializer_support_test.rb +0 -51
  71. data/test/serializer_test.rb +0 -1466
  72. data/test/test_fakes.rb +0 -218
@@ -1,231 +1,73 @@
1
+ require 'active_model/default_serializer'
2
+ require 'active_model/serializer'
3
+
1
4
  module ActiveModel
2
5
  class Serializer
3
- module Associations #:nodoc:
4
- class Config #:nodoc:
5
- class_attribute :options
6
-
7
- def self.refine(name, class_options)
8
- current_class = self
9
-
10
- Class.new(self) do
11
- singleton_class.class_eval do
12
- define_method(:to_s) do
13
- "(subclass of #{current_class.name})"
14
- end
15
-
16
- alias inspect to_s
17
- end
18
-
19
- self.options = class_options
20
-
21
- # cache the root so we can reuse it without falling back on a per-instance basis
22
- begin
23
- self.options[:root] ||= self.new(name, nil).root
24
- rescue
25
- # this could fail if it needs a valid source, for example a polymorphic association
26
- end
27
-
28
- end
29
- end
30
-
31
- self.options = {}
32
-
33
- def initialize(name, source, options={})
34
- @name = name
35
- @source = source
36
- @options = options
37
- end
38
-
39
- def option(key, default=nil)
40
- if @options.key?(key)
41
- @options[key]
42
- elsif self.class.options.key?(key)
43
- self.class.options[key]
44
- else
45
- default
46
- end
47
- end
48
-
49
- def target_serializer
50
- serializer = option(:serializer)
51
- serializer.is_a?(String) ? serializer.constantize : serializer
52
- end
53
-
54
- def source_serializer
55
- @source
56
- end
57
-
58
- def key
59
- option(:key) || @name
60
- end
61
-
62
- def root
63
- option(:root) || @name
64
- end
65
-
66
- def name
67
- option(:name) || @name
68
- end
69
-
70
- def associated_object
71
- option(:value) || source_serializer.send(name)
72
- end
73
-
74
- def embed_ids?
75
- [:id, :ids].include? option(:embed, source_serializer._embed)
76
- end
77
-
78
- def embed_objects?
79
- [:object, :objects].include? option(:embed, source_serializer._embed)
80
- end
81
-
82
- def embed_in_root?
83
- option(:include, source_serializer._root_embed)
84
- end
85
-
86
- def embeddable?
87
- !associated_object.nil?
88
- end
89
-
90
- protected
91
-
92
- def find_serializable(object)
93
- if target_serializer
94
- target_serializer.new(object, source_serializer.options)
95
- elsif object.respond_to?(:active_model_serializer) && (ams = object.active_model_serializer)
96
- ams.new(object, source_serializer.options)
97
- else
98
- object
99
- end
100
- end
6
+ class Association
7
+ def initialize(name, options={})
8
+ if options.has_key?(:include)
9
+ ActiveSupport::Deprecation.warn <<-WARN
10
+ ** Notice: include was renamed to embed_in_root. **
11
+ WARN
12
+ end
13
+
14
+ @name = name.to_s
15
+ @options = options
16
+ self.embed = options.fetch(:embed) { CONFIG.embed }
17
+ @embed_in_root = options.fetch(:embed_in_root) { options.fetch(:include) { CONFIG.embed_in_root } }
18
+ @embed_key = options[:embed_key] || :id
19
+ @key = options[:key]
20
+ @embedded_key = options[:root] || name
21
+
22
+ serializer = @options[:serializer]
23
+ @serializer_class = serializer.is_a?(String) ? serializer.constantize : serializer
101
24
  end
102
25
 
103
- class HasMany < Config #:nodoc:
104
- def key
105
- if key = option(:key)
106
- key
107
- elsif embed_ids?
108
- "#{@name.to_s.singularize}_ids".to_sym
109
- else
110
- @name
111
- end
112
- end
113
-
114
- def embed_key
115
- if key = option(:embed_key)
116
- key
117
- else
118
- :id
119
- end
120
- end
121
-
122
- def serialize
123
- associated_object.map do |item|
124
- find_serializable(item).serializable_hash
125
- end
126
- end
127
-
128
- def serializables
129
- associated_object.map do |item|
130
- find_serializable(item)
131
- end
132
- end
26
+ attr_reader :name, :embed_ids, :embed_objects
27
+ attr_accessor :embed_in_root, :embed_key, :key, :embedded_key, :root_key, :serializer_class, :options
28
+ alias embed_ids? embed_ids
29
+ alias embed_objects? embed_objects
30
+ alias embed_in_root? embed_in_root
133
31
 
134
- def serialize_ids
135
- ids_key = "#{@name.to_s.singularize}_ids".to_sym
136
- if !option(:embed_key) && !source_serializer.respond_to?(@name.to_s) && source_serializer.object.respond_to?(ids_key)
137
- source_serializer.object.read_attribute_for_serialization(ids_key)
138
- else
139
- associated_object.map do |item|
140
- item.read_attribute_for_serialization(embed_key)
141
- end
142
- end
143
- end
32
+ def embed=(embed)
33
+ @embed_ids = embed == :id || embed == :ids
34
+ @embed_objects = embed == :object || embed == :objects
144
35
  end
145
36
 
146
- class HasOne < Config #:nodoc:
147
- def embeddable?
148
- if polymorphic? && associated_object.nil?
149
- false
150
- else
151
- true
152
- end
153
- end
37
+ def build_serializer(object, options = {})
38
+ @serializer_class.new(object, options.merge(@options))
39
+ end
154
40
 
155
- def polymorphic?
156
- option :polymorphic
41
+ class HasOne < Association
42
+ def initialize(name, *args)
43
+ super
44
+ @root_key = @embedded_key.to_s.pluralize
45
+ @key ||= "#{name}_id"
157
46
  end
158
47
 
159
- def root
160
- if root = option(:root)
161
- root
162
- elsif polymorphic?
163
- associated_object.class.to_s.pluralize.demodulize.underscore.to_sym
164
- else
165
- @name.to_s.pluralize.to_sym
166
- end
48
+ def build_serializer(object, options = {})
49
+ @serializer_class ||= Serializer.serializer_for(object) || DefaultSerializer
50
+ options[:_wrap_in_array] = embed_in_root?
51
+ super
167
52
  end
53
+ end
168
54
 
169
- def key
170
- if key = option(:key)
171
- key
172
- elsif embed_ids? && !polymorphic?
173
- "#{@name}_id".to_sym
174
- else
175
- @name
176
- end
55
+ class HasMany < Association
56
+ def initialize(name, *args)
57
+ super
58
+ @root_key = @embedded_key
59
+ @key ||= "#{name.to_s.singularize}_ids"
177
60
  end
178
61
 
179
- def embed_key
180
- if key = option(:embed_key)
181
- key
62
+ def build_serializer(object, options = {})
63
+ if @serializer_class && !(@serializer_class <= ArraySerializer)
64
+ @options[:each_serializer] = @serializer_class
65
+ @serializer_class = ArraySerializer
182
66
  else
183
- :id
67
+ @serializer_class ||= ArraySerializer
184
68
  end
185
- end
186
-
187
- def polymorphic_key
188
- associated_object.class.to_s.demodulize.underscore.to_sym
189
- end
190
69
 
191
- def serialize
192
- object = associated_object
193
-
194
- if object && polymorphic?
195
- {
196
- :type => polymorphic_key,
197
- polymorphic_key => find_serializable(object).serializable_hash
198
- }
199
- elsif object
200
- find_serializable(object).serializable_hash
201
- end
202
- end
203
-
204
- def serializables
205
- object = associated_object
206
- value = object && find_serializable(object)
207
- value ? [value] : []
208
- end
209
-
210
- def serialize_ids
211
- id_key = "#{@name}_id".to_sym
212
-
213
- if polymorphic?
214
- if associated_object
215
- {
216
- :type => polymorphic_key,
217
- :id => associated_object.read_attribute_for_serialization(embed_key)
218
- }
219
- else
220
- nil
221
- end
222
- elsif !option(:embed_key) && !source_serializer.respond_to?(@name.to_s) && source_serializer.object.respond_to?(id_key)
223
- source_serializer.object.read_attribute_for_serialization(id_key)
224
- elsif associated_object
225
- associated_object.read_attribute_for_serialization(embed_key)
226
- else
227
- nil
228
- end
70
+ super
229
71
  end
230
72
  end
231
73
  end
@@ -0,0 +1,31 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Config
4
+ def initialize(data = {})
5
+ @data = data
6
+ end
7
+
8
+ def each(&block)
9
+ @data.each(&block)
10
+ end
11
+
12
+ def clear
13
+ @data.clear
14
+ end
15
+
16
+ def method_missing(name, *args)
17
+ name = name.to_s
18
+ return @data[name] if @data.include?(name)
19
+ match = name.match(/\A(.*?)([?=]?)\Z/)
20
+ case match[2]
21
+ when "="
22
+ @data[match[1]] = args.first
23
+ when "?"
24
+ !!@data[match[1]]
25
+ end
26
+ end
27
+ end
28
+
29
+ CONFIG = Config.new('embed' => :objects) # :nodoc:
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/rails/resource/resource_generator'
3
+
4
+ module Rails
5
+ module Generators
6
+ class ResourceGenerator
7
+ def add_serializer
8
+ invoke 'serializer'
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -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,37 @@
1
+ module Rails
2
+ module Generators
3
+ class SerializerGenerator < NamedBase
4
+ source_root File.expand_path('../templates', __FILE__)
5
+ check_class_collision suffix: 'Serializer'
6
+
7
+ argument :attributes, type: :array, default: [], banner: 'field:type field:type'
8
+
9
+ class_option :parent, type: :string, desc: 'The parent class for the generated serializer'
10
+
11
+ def create_serializer_file
12
+ template 'serializer.rb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb")
13
+ end
14
+
15
+ private
16
+
17
+ def attributes_names
18
+ [:id] + attributes.select { |attr| !attr.reference? }.map { |a| a.name.to_sym }
19
+ end
20
+
21
+ def association_names
22
+ attributes.select { |attr| attr.reference? }.map { |a| a.name.to_sym }
23
+ end
24
+
25
+ def parent_class_name
26
+ if options[:parent]
27
+ options[:parent]
28
+ elsif (ns = Rails::Generators.namespace) && ns.const_defined?(:ApplicationSerializer) ||
29
+ defined?(::ApplicationSerializer)
30
+ 'ApplicationSerializer'
31
+ else
32
+ 'ActiveModel::Serializer'
33
+ end
34
+ end
35
+ end
36
+ end
37
+ 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 -%>