active_model_serializers 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e52d190b9a08bf39665a61da10e5c1103b553016
4
- data.tar.gz: 5ca66e11bde1221cee716a40b48b71f3f3600a09
3
+ metadata.gz: 806e6040b82178f17890aaa7d76e431ebf41fac6
4
+ data.tar.gz: 68562583b4d96f5e7baf0e35f86d39d6f8267cb1
5
5
  SHA512:
6
- metadata.gz: 95648244da95ed7a5d69aa91079a89098864d1fe7d4b7d827caeb3b45e019f4e07ddf563c34ee154eb43e9501ac485ba624e6abef6c131cb8fbf79a9bfefc4b8
7
- data.tar.gz: 727fb1fa750281605e22c8830212304eb83ac209c2ab2743e7f8695d763eee5635c97d2db6994678b77620385c50899583fd552eb94ffe821c29d1ba10c5d9cb
6
+ metadata.gz: 33e9ffa81d5121eb929e83e50c80b16fb68a322210dddcaea36396ea18ac4fb1c2862ff025a175fe87e7a70d85bc5208866e7ee2f3aa3093dda503a229652429
7
+ data.tar.gz: 672a0655b5d4dd8d93003d4c18d6b2606e804d381fbe3d5777d46ec04711ae5a81a26a95b34c9d796c4ceb34abd800f2685f343c33f60a23a544df9db6754ad9
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ *.swp
data/.travis.yml CHANGED
@@ -1,11 +1,9 @@
1
1
  language: ruby
2
- before_install:
3
- - gem install bundler
4
2
  rvm:
5
3
  - 1.8.7
6
4
  - 1.9.2
7
5
  - 1.9.3
8
- - ruby-head
6
+ - 2.0.0
9
7
  - ree
10
8
  - jruby-18mode
11
9
  - jruby-19mode
@@ -15,6 +13,8 @@ gemfile:
15
13
  - Gemfile
16
14
  - Gemfile.edge
17
15
  matrix:
16
+ allow_failures:
17
+ - gemfile: Gemfile.edge
18
18
  exclude:
19
19
  # Edge Rails is only compatible with 1.9.3
20
20
  - gemfile: Gemfile.edge
data/CHANGELOG.md ADDED
@@ -0,0 +1,61 @@
1
+ # UNRELEASED
2
+
3
+ # VERSION 0.8.0
4
+
5
+ * Attributes can now have optional types.
6
+
7
+ * A new DefaultSerializer ensures that POROs behave the same way as ActiveModels.
8
+
9
+ * If you wish to override ActiveRecord::Base#to_Json, you can now require
10
+ 'active_record/serializer_override'. We don't recommend you do this, but
11
+ many users do, so we've left it optional.
12
+
13
+ * Fixed a bug where ActionController wouldn't always have MimeResponds.
14
+
15
+ * An optinal caching feature allows you to cache JSON & hashes that AMS uses.
16
+ Adding 'cached true' to your Serializers will turn on this cache.
17
+
18
+ * URL helpers used inside of Engines now work properly.
19
+
20
+ * Serializers now can filter attributes with `only` and `except`:
21
+
22
+ UserSerializer.new(user, only: [:first_name, :last_name])
23
+ UserSerializer.new(user, except: :first_name)
24
+
25
+ * Basic Mongoid support. We now include our mixins in the right place.
26
+
27
+ * On Ruby 1.8, we now generate an `id` method that properly serializes `id`
28
+ columns. See issue #127 for more.
29
+
30
+ * Add an alias for `scope` method to be the name of the context. By default
31
+ this is `current_user`. The name is automatically set when using
32
+ `serialization_scope` in the controller.
33
+
34
+ * Pass through serialization options (such as `:include`) when a model
35
+ has no serializer defined.
36
+
37
+ # VERSION 0.7.0
38
+
39
+ * ```embed_key``` option to allow embedding by attributes other than IDs
40
+ * Fix rendering nil with custom serializer
41
+ * Fix global ```self.root = false```
42
+ * Add support for specifying the serializer for an association as a String
43
+ * Able to specify keys on the attributes method
44
+ * Serializer Reloading via ActiveSupport::DescendantsTracker
45
+ * Reduce double map to once; Fixes datamapper eager loading.
46
+
47
+ # VERSION 0.6.0
48
+
49
+ * Serialize sets properly
50
+ * Add root option to ArraySerializer
51
+ * Support polymorphic associations
52
+ * Support :each_serializer in ArraySerializer
53
+ * Add `scope` method to easily access the scope in the serializer
54
+ * Fix regression with Rails 3.2.6; add Rails 4 support
55
+ * Allow serialization_scope to be disabled with serialization_scope nil
56
+ * Array serializer should support pure ruby objects besides serializers
57
+
58
+ # VERSION 0.5.0 (May 16, 2012)
59
+
60
+ * First tagged version
61
+ * Changes generators to always generate an ApplicationSerializer
data/DESIGN.textile CHANGED
@@ -217,7 +217,7 @@ First, we specified the list of included attributes at the top of the class. Thi
217
217
 
218
218
  NOTE: Internally, +ActiveModel::Serializer+ uses +read_attribute_for_serialization+, which defaults to +read_attribute+, which defaults to +send+. So if you're rolling your own models for use with the serializer, you can use simple Ruby accessors for your attributes if you like.
219
219
 
220
- Next, we use the attributes methood in our +serializable_hash+ method, which allowed us to eliminate the +post+ method we hand-rolled
220
+ Next, we use the attributes method in our +serializable_hash+ method, which allowed us to eliminate the +post+ method we hand-rolled
221
221
  earlier. We could also eliminate the +as_json+ method, as +ActiveModel::Serializer+ provides a default +as_json+ method for
222
222
  us that calls our +serializable_hash+ method and inserts a root. But we can go a step further!
223
223
 
@@ -354,7 +354,7 @@ h4. Modifying Associations
354
354
  You can also rename associations if required. Say for example you have an association that
355
355
  makes sense to be named one thing in your code, but another when data is serialized.
356
356
  You can use the <code:key</code> option to specify a different name for an association.
357
- Here is an exmaple:
357
+ Here is an example:
358
358
 
359
359
  <pre lang="ruby">
360
360
  class UserSerializer < ActiveModel::Serializer
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify gem dependencies in active_model_serializers.gemspec
4
4
  gemspec
5
+
6
+ gem "coveralls", :require => false
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://secure.travis-ci.org/rails-api/active_model_serializers.png)](http://travis-ci.org/rails-api/active_model_serializers)
1
+ [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png)](https://travis-ci.org/rails-api/active_model_serializers) [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers) [![Coverage Status](https://coveralls.io/repos/rails-api/active_model_serializers/badge.png?branch=master)](https://coveralls.io/r/rails-api/active_model_serializers)
2
2
 
3
3
  # Purpose
4
4
 
@@ -10,16 +10,16 @@ Serializers know about both a model and the `current_user`, so you can
10
10
  customize serialization based upon whether a user is authorized to see the
11
11
  content.
12
12
 
13
- In short, **serializers replaces hash-driven development with object-oriented
13
+ In short, **serializers replace hash-driven development with object-oriented
14
14
  development.**
15
15
 
16
16
  # Installing Serializers
17
17
 
18
- For now, the easiest way to install `ActiveModel::Serializers` is to add it
19
- to your `Gemfile`:
18
+ The easiest way to install `ActiveModel::Serializers` is to add it to your
19
+ `Gemfile`:
20
20
 
21
21
  ```ruby
22
- gem "active_model_serializers", "~> 0.6.0"
22
+ gem "active_model_serializers", "~> 0.7.0"
23
23
  ```
24
24
 
25
25
  Then, install it on the command line:
@@ -48,10 +48,14 @@ $ rails g serializer post
48
48
  ### Support for PORO's and other ORM's.
49
49
 
50
50
  Currently `ActiveModel::Serializers` adds serialization support to all models
51
- that descend from `ActiveRecord`. If you are using another ORM or if you are
52
- using objects that are `ActiveModel` compliant, but do not descend from
53
- `ActiveRecord`. You must add an include statement for
54
- `ActiveModel::SerializerSupport`.
51
+ that descend from `ActiveRecord` or include `Mongoid::Document`. If you are
52
+ using another ORM, or if you are using objects that are `ActiveModel`
53
+ compliant but do not descend from `ActiveRecord` or include
54
+ `Mongoid::Document`, you must add an include statement for
55
+ `ActiveModel::SerializerSupport` to make models serializable. If you
56
+ also want to make collections serializable, you should include
57
+ `ActiveModel::ArraySerializationSupport` into your ORM's
58
+ relation/criteria class.
55
59
 
56
60
  # ActiveModel::Serializer
57
61
 
@@ -135,7 +139,7 @@ render :json => @posts, :root => "some_posts"
135
139
  ```
136
140
 
137
141
  You may disable the root element for arrays at the top level, which will result in
138
- more concise json. To disable the root element for arrays, you have 3 options:
142
+ more concise json. To disable the root element for arrays, you have 4 options:
139
143
 
140
144
  #### 1. Disable root globally for in `ArraySerializer`. In an initializer:
141
145
 
@@ -181,7 +185,7 @@ render :json => @posts, :each_serializer => FancyPostSerializer
181
185
 
182
186
  If you define `default_serializer_options` method in your controller,
183
187
  all serializers in actions of this controller and it's children will use them.
184
- One of options may be `root: false`
188
+ One of the options may be `root: false`
185
189
 
186
190
  ```ruby
187
191
  def default_serializer_options
@@ -232,22 +236,36 @@ end
232
236
  Within a serializer's methods, you can access the object being
233
237
  serialized as `object`.
234
238
 
235
- You can also access the `scope` method, which provides an
236
- authorization context to your serializer. By default, scope
239
+ You can also access the `current_user` method, which provides an
240
+ authorization context to your serializer. By default, the context
237
241
  is the current user of your application, but this
238
242
  [can be customized](#customizing-scope).
239
243
 
240
244
  Serializers will check for the presence of a method named
241
245
  `include_[ATTRIBUTE]?` to determine whether a particular attribute should be
242
246
  included in the output. This is typically used to customize output
243
- based on `scope`. For example:
247
+ based on `current_user`. For example:
244
248
 
245
249
  ```ruby
246
250
  class PostSerializer < ActiveModel::Serializer
247
251
  attributes :id, :title, :body, :author
248
252
 
249
253
  def include_author?
250
- scope.admin?
254
+ current_user.admin?
255
+ end
256
+ end
257
+ ```
258
+
259
+ The type of a computed attribute (like :full_name above) is not easily
260
+ calculated without some sophisticated static code analysis. To specify the
261
+ type of a computed attribute:
262
+
263
+ ```ruby
264
+ class PersonSerializer < ActiveModel::Serializer
265
+ attributes :first_name, :last_name, {:full_name => :string}
266
+
267
+ def full_name
268
+ "#{object.first_name} #{object.last_name}"
251
269
  end
252
270
  end
253
271
  ```
@@ -311,7 +329,7 @@ class PersonSerializer < ActiveModel::Serializer
311
329
 
312
330
  def attributes
313
331
  hash = super
314
- if scope.admin?
332
+ if current_user.admin?
315
333
  hash["ssn"] = object.ssn
316
334
  hash["secret"] = object.mothers_maiden_name
317
335
  end
@@ -339,7 +357,7 @@ class PostSerializer < ActiveModel::Serializer
339
357
 
340
358
  # only let the user see comments he created.
341
359
  def comments
342
- object.comments.where(:created_by => scope)
360
+ object.comments.where(:created_by => current_user)
343
361
  end
344
362
  end
345
363
  ```
@@ -381,7 +399,7 @@ class PostSerializer < ActiveModel::Serializer
381
399
  has_many :comments
382
400
 
383
401
  def include_associations!
384
- include! :author if scope.admin?
402
+ include! :author if current_user.admin?
385
403
  include! :comments unless object.comments_disabled?
386
404
  end
387
405
  end
@@ -394,6 +412,8 @@ You may also use the `:serializer` option to specify a custom serializer class a
394
412
  has_one :reviewer, :polymorphic => true
395
413
  ```
396
414
 
415
+ Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
416
+
397
417
  ## Embedding Associations
398
418
 
399
419
  By default, associations will be embedded inside the serialized object. So if
@@ -573,7 +593,7 @@ Ajax requests, you probably just want to use the default embedded behavior.
573
593
 
574
594
  ## Customizing Scope
575
595
 
576
- In a serializer, `scope` is the current authorization scope which the controller
596
+ In a serializer, `current_user` is the current authorization scope which the controller
577
597
  provides to the serializer when you call `render :json`. By default, this is
578
598
  `current_user`, but can be customized in your controller by calling
579
599
  `serialization_scope`:
@@ -584,6 +604,9 @@ class ApplicationController < ActionController::Base
584
604
  end
585
605
  ```
586
606
 
607
+ The above example will also change the scope name from `current_user` to
608
+ `current_admin`.
609
+
587
610
  Please note that, until now, `serialization_scope` doesn't accept a second
588
611
  object with options for specifying which actions should or should not take a
589
612
  given scope in consideration.
@@ -612,12 +635,12 @@ class CitiesController < ApplicationController
612
635
  def show
613
636
  @city = City.find(params[:id])
614
637
 
615
- render :json => @city, :scope => current_admin?
638
+ render :json => @city, :scope => current_admin, :scope_name => :current_admin
616
639
  end
617
640
  end
618
641
  ```
619
642
 
620
- Assuming that the `current_admin?` method needs to make a query in the database
643
+ Assuming that the `current_admin` method needs to make a query in the database
621
644
  for the current user, the advantage of this approach is that, by setting
622
645
  `serialization_scope` to `nil`, the `index` action no longer will need to make
623
646
  that query, only the `show` action will.
data/Rakefile CHANGED
@@ -10,4 +10,9 @@ Rake::TestTask.new(:test) do |t|
10
10
  t.verbose = true
11
11
  end
12
12
 
13
+ desc 'Benchmark'
14
+ task :bench do
15
+ load 'bench/perf.rb'
16
+ end
17
+
13
18
  task :default => :test
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
8
8
  gem.email = ["jose.valim@gmail.com", "wycats@gmail.com"]
9
9
  gem.description = %q{Making it easy to serialize models for client-side use}
10
10
  gem.summary = %q{Bringing consistency and object orientation to model serialization. Works great for client-side MVC frameworks!}
11
- gem.homepage = "https://github.com/josevalim/active_model_serializers"
11
+ gem.homepage = "https://github.com/rails-api/active_model_serializers"
12
12
 
13
13
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
14
  gem.files = `git ls-files`.split("\n")
@@ -21,4 +21,5 @@ Gem::Specification.new do |gem|
21
21
  gem.add_development_dependency "rails", ">= 3.0"
22
22
  gem.add_development_dependency "pry"
23
23
  gem.add_development_dependency "simplecov"
24
+ gem.add_development_dependency "coveralls"
24
25
  end
data/bench/perf.rb ADDED
@@ -0,0 +1,43 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require "active_model_serializers"
4
+ require "active_support/json"
5
+ require "benchmark"
6
+
7
+ class User < Struct.new(:id,:name,:age,:about)
8
+ include ActiveModel::SerializerSupport
9
+
10
+ def fast_hash
11
+ h = {
12
+ id: read_attribute_for_serialization(:id),
13
+ name: read_attribute_for_serialization(:name),
14
+ about: read_attribute_for_serialization(:about)
15
+ }
16
+ h[:age] = read_attribute_for_serialization(:age) if age > 18
17
+ h
18
+ end
19
+ end
20
+
21
+ class UserSerializer < ActiveModel::Serializer
22
+ attributes :id, :name, :age, :about
23
+
24
+ def include_age?
25
+ object.age > 18
26
+ end
27
+ end
28
+
29
+
30
+
31
+ u = User.new(1, "sam", 10, "about")
32
+ s = UserSerializer.new(u)
33
+
34
+ n = 100000
35
+
36
+ Benchmark.bmbm {|x|
37
+ x.report("init") { n.times { UserSerializer.new(u) } }
38
+ x.report("fast_hash") { n.times { u.fast_hash } }
39
+ x.report("attributes") { n.times { UserSerializer.new(u).attributes } }
40
+ x.report("serializable_hash") { n.times { UserSerializer.new(u).serializable_hash } }
41
+ }
42
+
43
+
@@ -2,7 +2,7 @@ module ActionController
2
2
  # Action Controller Serialization
3
3
  #
4
4
  # Overrides render :json to check if the given object implements +active_model_serializer+
5
- # as a method. If so, use the returned serializer instead of calling +to_json+ in the object.
5
+ # as a method. If so, use the returned serializer instead of calling +to_json+ on the object.
6
6
  #
7
7
  # This module also provides a serialization_scope method that allows you to configure the
8
8
  # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+
@@ -33,37 +33,20 @@ module ActionController
33
33
  end
34
34
 
35
35
  def serialization_scope
36
- send(_serialization_scope) if _serialization_scope && respond_to?(_serialization_scope)
36
+ send(_serialization_scope) if _serialization_scope && respond_to?(_serialization_scope, true)
37
37
  end
38
38
 
39
39
  def default_serializer_options
40
40
  end
41
41
 
42
- def _render_option_json(json, options)
43
- options = default_serializer_options.merge(options) if default_serializer_options
42
+ def _render_option_json(resource, options)
43
+ json = ActiveModel::Serializer.build_json(self, resource, options)
44
44
 
45
- serializer = options.delete(:serializer) ||
46
- (json.respond_to?(:active_model_serializer) && json.active_model_serializer)
47
-
48
- if json.respond_to?(:to_ary)
49
- unless serializer <= ActiveModel::ArraySerializer
50
- raise ArgumentError.new("#{serializer.name} is not an ArraySerializer. " +
51
- "You may want to use the :each_serializer option instead.")
52
- end
53
-
54
- if options[:root] != false && serializer.root != false
55
- # default root element for arrays is serializer's root or the controller name
56
- # the serializer for an Array is ActiveModel::ArraySerializer
57
- options[:root] ||= serializer.root || controller_name
58
- end
59
- end
60
-
61
- if serializer
62
- options[:scope] = serialization_scope unless options.has_key?(:scope)
63
- options[:url_options] = url_options
64
- json = serializer.new(json, options)
45
+ if json
46
+ super(json, options)
47
+ else
48
+ super
65
49
  end
66
- super
67
50
  end
68
51
 
69
52
  module ClassMethods
@@ -5,7 +5,7 @@ require 'active_support/descendants_tracker'
5
5
  module ActiveModel
6
6
  # Active Model Array Serializer
7
7
  #
8
- # It serializes an Array, checking if each element that implements
8
+ # Serializes an Array, checking if each element implements
9
9
  # the +active_model_serializer+ method.
10
10
  #
11
11
  # To disable serialization of root elements:
@@ -19,28 +19,20 @@ module ActiveModel
19
19
 
20
20
  class_attribute :root
21
21
 
22
- def initialize(object, options={})
23
- @object, @options = object, options
24
- end
25
-
26
- def serializable_array
27
- @object.map do |item|
28
- if @options.has_key? :each_serializer
29
- serializer = @options[:each_serializer]
30
- elsif item.respond_to?(:active_model_serializer)
31
- serializer = item.active_model_serializer
32
- end
22
+ class_attribute :cache
23
+ class_attribute :perform_caching
33
24
 
34
- serializable = serializer ? serializer.new(item, @options) : item
35
-
36
- if serializable.respond_to?(:serializable_hash)
37
- serializable.serializable_hash
38
- else
39
- serializable.as_json
40
- end
25
+ class << self
26
+ # set perform caching like root
27
+ def cached(value = true)
28
+ self.perform_caching = value
41
29
  end
42
30
  end
43
31
 
32
+ def initialize(object, options={})
33
+ @object, @options = object, options
34
+ end
35
+
44
36
  def meta_key
45
37
  @options[:meta_key].try(:to_sym) || :meta
46
38
  end
@@ -61,6 +53,52 @@ module ActiveModel
61
53
  serializable_array
62
54
  end
63
55
  end
64
- end
65
56
 
57
+ def to_json(*args)
58
+ if perform_caching?
59
+ cache.fetch expand_cache_key([self.class.to_s.underscore, cache_key, 'to-json']) do
60
+ super
61
+ end
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ def serializable_array
68
+ if perform_caching?
69
+ cache.fetch expand_cache_key([self.class.to_s.underscore, cache_key, 'serializable-array']) do
70
+ _serializable_array
71
+ end
72
+ else
73
+ _serializable_array
74
+ end
75
+ end
76
+
77
+ private
78
+ def _serializable_array
79
+ @object.map do |item|
80
+ if @options.has_key? :each_serializer
81
+ serializer = @options[:each_serializer]
82
+ elsif item.respond_to?(:active_model_serializer)
83
+ serializer = item.active_model_serializer
84
+ end
85
+
86
+ serializable = serializer ? serializer.new(item, @options) : DefaultSerializer.new(item, @options)
87
+
88
+ if serializable.respond_to?(:serializable_hash)
89
+ serializable.serializable_hash
90
+ else
91
+ serializable.as_json
92
+ end
93
+ end
94
+ end
95
+
96
+ def expand_cache_key(*args)
97
+ ActiveSupport::Cache.expand_cache_key(args)
98
+ end
99
+
100
+ def perform_caching?
101
+ perform_caching && cache && respond_to?(:cache_key)
102
+ end
103
+ end
66
104
  end