active_model_serializers 0.8.4 → 0.9.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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 -%>