undo-serializer-active_model 0.0.4 → 0.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8f6129e916df1177c739956b56adcfb3b1f337b
4
- data.tar.gz: 09f23cd4001f82babd417949d40dced4323e359e
3
+ metadata.gz: a295ea13f84f57828d1f8f160c46776e92cff623
4
+ data.tar.gz: 65880e189d3907d9a4ff5ca31fe3f9fca9156619
5
5
  SHA512:
6
- metadata.gz: 4d219d7d0189d6eeb4bd05bc144e663c543aaeb902d1832545e7b0b4003a353a7514a24cf5d9fdc7417f39dfc57675bc68ea320d6cadf073c89373ccc4c7c034
7
- data.tar.gz: afda0a79e726b1656deaed0c48d73f6f6052d5414560c91d21a2f9804e20d7d574afa44c3b737bbfa6f7ea7fbba944bd6df5d3c4f8bea346db63bc1b24224e03
6
+ metadata.gz: d995320ec3063c1f92e11062ce5f75190f78ae98313ad6f8cb5eeef8a54ff7cc64621af57ec3048da98d4ad9353866503cacb024e047b53252a5124d335d26a2
7
+ data.tar.gz: 6d197310535c87d08e0f27564ee6b2be3fc4854b0fd5e6c3289250edcb5566cdb71cf1e92b7d4a557ebbf8f894adbf124cb3a989cdd8c9379c08c678244b72ac
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.1.0-p0
1
+ 2.1.1
data/Gemfile CHANGED
@@ -21,7 +21,6 @@ group :test, :development do
21
21
  gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
22
22
  gem 'factory_girl'
23
23
  gem 'faker'
24
- gem "active_model_serializers", "~> 0.8"
25
24
  end
26
25
 
27
26
  gemspec
data/README.md CHANGED
@@ -4,10 +4,9 @@ Undo
4
4
  [![Gemnasium Build Status](https://gemnasium.com/AlexParamonov/undo-serializer-active_model.png)](http://gemnasium.com/AlexParamonov/undo-serializer-active_model)
5
5
  [![Coverage Status](https://coveralls.io/repos/AlexParamonov/undo-serializer-active_model/badge.png?branch=master)](https://coveralls.io/r/AlexParamonov/undo-serializer-active_model?branch=master)
6
6
  [![Gem Version](https://badge.fury.io/rb/undo-serializer-active_model.png)](http://badge.fury.io/rb/undo-serializer-active_model)
7
+ [![Code Climate](https://codeclimate.com/github/AlexParamonov/undo-serializer-active_model.png)](https://codeclimate.com/github/AlexParamonov/undo-serializer-active_model)
7
8
 
8
- ActiveModel serializer for Undo gem.
9
-
10
- Designed to be used with `gem "active_model_serializers"`, but does not depends on it.
9
+ ActiveModel serializer for Undo gem. Does not require anything from Rails so is friendly to use with POROs.
11
10
 
12
11
  Contents
13
12
  ---------
@@ -33,16 +32,19 @@ Or install it yourself as:
33
32
 
34
33
  $ gem install undo-serializer-active_model
35
34
 
35
+ Most likely you'll install undo gem as well:
36
+
37
+ $ gem install undo
38
+
36
39
  Requirements
37
40
  ------------
38
41
  1. Ruby >= 1.9
39
- 1. `activesupport` (`active_model_serializers` depends on it)
40
42
 
41
43
  Usage
42
44
  ------------
43
45
 
44
46
  Gem is designed to be used with Undo gem.
45
- Add it in global config:
47
+ Customize Undo to use serializer in global configuration:
46
48
 
47
49
  ``` ruby
48
50
  Undo.configure do |config|
@@ -50,43 +52,36 @@ Undo.configure do |config|
50
52
  end
51
53
  ```
52
54
 
53
- Custom serializer could be provided to the adapter:
55
+ Custom primary_key set, find_or_initialize and persist `Proc`s could be provided to the adapter:
54
56
  ``` ruby
55
57
  Undo.configure do |config|
56
- config.serializer =
57
- Undo::Serializer::ActiveModel.new serializer: ->(object) { "#{object.class.name}UndoSerializer".constantize.new(object) }
58
+ config.serializer = Undo::Serializer::ActiveModel.new(
59
+ primary_key: [:id, :status],
60
+ find_or_initialize: ->(object_class, pk_attributes) { object_class.find_or_initialize_by pk_attributes },
61
+ serialize_attributes: ->(object) { object.serializable_hash },
62
+ persist: ->(object) { object.save! },
63
+ )
58
64
  end
59
65
  ```
60
66
 
61
- Or it may be initialized by serializer instance:
62
- ``` ruby
63
- Undo.configure do |config|
64
- config.serializer =
65
- Undo::Serializer::ActiveModel.new CustomSerializer.new
66
- end
67
- ```
67
+ For ActiveRecord Undo uses reasonable defaults, so most of the time it is not needed to overwrite them.
68
+ It should work with most Virtus objects as well.
68
69
 
69
- As usual any Undo configuration may be set in place on wrap and restore:
70
+ As usual any Undo configuration may be set in place on store, wrap and restore:
70
71
  ``` ruby
71
- Undo.wrap user, serializer: Undo::Serializer::ActiveModel.new
72
- Undo.restore uuid, serializer: Undo::Serializer::ActiveModel.new
73
- ```
74
-
75
- In place using the specific serializer from `gem "active_model_serializers"`:
76
- ``` ruby
77
- Undo.wrap user, serializer: Undo::Serializer::ActiveModel.new(UserSerializer.new(user))
72
+ Undo.store user, serializer: Undo::Serializer::ActiveRecord.new(primary_key: :uuid)
73
+ Undo.restore uuid, primary_key: :uuid, persist: ->(object) { object.write_to_disk! }
78
74
  ```
79
75
 
80
76
  ### Associations
81
77
 
82
- It is required to set `somethig___association_class_name` as `key` in `active_model_serializer`:
78
+ Add `include` option to serialize the association
83
79
  ``` ruby
84
- class UserSerializer < ActiveModel::Serializer
85
- attributes *User.attribute_names.map(&:to_sym)
86
- has_many :roles, key: :has_many___roles
87
- end
80
+ uuid = Undo.store post, include: comments
81
+ Undo.restore uuid
88
82
  ```
89
83
 
84
+ Will restore post with related comments.
90
85
 
91
86
  Contacts
92
87
  -------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.1.0
data/gemfiles/3.0.gemfile CHANGED
@@ -25,7 +25,6 @@ group :test, :development do
25
25
  gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
26
26
  gem "factory_girl"
27
27
  gem "faker"
28
- gem "active_model_serializers", "~> 0.8"
29
28
  end
30
29
 
31
30
  gemspec :path=>".././"
data/gemfiles/3.1.gemfile CHANGED
@@ -25,7 +25,6 @@ group :test, :development do
25
25
  gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
26
26
  gem "factory_girl"
27
27
  gem "faker"
28
- gem "active_model_serializers", "~> 0.8"
29
28
  end
30
29
 
31
30
  gemspec :path=>".././"
data/gemfiles/3.2.gemfile CHANGED
@@ -25,7 +25,6 @@ group :test, :development do
25
25
  gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
26
26
  gem "factory_girl"
27
27
  gem "faker"
28
- gem "active_model_serializers", "~> 0.8"
29
28
  end
30
29
 
31
30
  gemspec :path=>".././"
data/gemfiles/4.0.gemfile CHANGED
@@ -25,7 +25,6 @@ group :test, :development do
25
25
  gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
26
26
  gem "factory_girl"
27
27
  gem "faker"
28
- gem "active_model_serializers", "~> 0.8"
29
28
  end
30
29
 
31
30
  gemspec :path=>".././"
data/gemfiles/4.1.gemfile CHANGED
@@ -25,7 +25,6 @@ group :test, :development do
25
25
  gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
26
26
  gem "factory_girl"
27
27
  gem "faker"
28
- gem "active_model_serializers", "~> 0.8"
29
28
  end
30
29
 
31
30
  gemspec :path=>".././"
@@ -1,65 +1,124 @@
1
- require "active_support"
2
-
3
1
  module Undo
4
2
  module Serializer
5
3
  class ActiveModel
6
- def initialize(*args)
7
- options = args.extract_options!
8
- @serializer = args.first
9
- @serializer_source = options.fetch :serializer,
10
- ->(object) { object.active_model_serializer.new object }
4
+ def initialize(options = {})
5
+ load_options options
11
6
  end
12
7
 
13
- def serialize(object)
14
- serializer(object).as_json
8
+ def serialize(object, options = {})
9
+ return object.map do |record|
10
+ serialize record, options
11
+ end if array? object
12
+
13
+ load_options options
14
+
15
+ attributes = serialize_attributes(object) || {}
16
+ associations = {}
17
+ Array(options[:include]).map do |association|
18
+ associations[association] = serialize(object.public_send association)
19
+ end
20
+ pk_attributes = symbolize_keys(attributes).select do |attribute|
21
+ primary_key_fields.include? attribute
22
+ end
23
+
24
+ {
25
+ attributes: attributes,
26
+ associations: associations,
27
+ meta: {
28
+ pk_attributes: pk_attributes,
29
+ class_name: object.class.name,
30
+ }
31
+ }
15
32
  end
16
33
 
17
- def deserialize(hash)
18
- object_handler, data = hash.first
19
- return unless data.is_a? Hash
20
- data.stringify_keys!
34
+ def deserialize(object, options = {})
35
+ return object.map do |record|
36
+ deserialize record
37
+ end if array? object
38
+
39
+ load_options options
21
40
 
22
- ActiveRecord::Base.transaction do
23
- initialize_object(object_handler, data).tap do |object|
24
- data.each do |field, value|
25
- next if "id" == field && object.persisted?
41
+ hash = symbolize_keys object
42
+ object_meta = hash.fetch :meta
43
+ associations = hash.fetch :associations
44
+ attributes = hash.fetch :attributes
26
45
 
27
- case field
28
- when /___(.*)/ then deserialize_association object, $1, value
29
- else deserialize_field object, field, value
30
- end
46
+ with_transaction do
47
+ initialize_object(object_meta).tap do |object|
48
+ attributes.each do |field, value|
49
+ deserialize_field object, field, value
31
50
  end
32
51
 
33
- object.save!
52
+ # QUESTION: Set associations? object.association_name = deserialize association ?
53
+ associations.each do |(association_name, association)|
54
+ deserialize association
55
+ end
56
+
57
+ persist object
34
58
  end
35
59
  end
36
-
37
60
  end
38
61
 
39
62
  private
40
- attr_reader :serializer_source
63
+ attr_reader :serialize_attributes_source,
64
+ :initialize_object_source,
65
+ :persist_object_source
41
66
 
42
- def serializer(object)
43
- @serializer ||= serializer_source.call object
67
+ def deserialize_field(object, field, value)
68
+ object.send "#{field}=", value # not public_send!
44
69
  end
45
70
 
46
- def deserialize_association(object, association, values)
47
- Array.wrap(values).each do |value|
48
- deserialize object.public_send(association) => value
71
+ def initialize_object(meta)
72
+ object_class = constantize meta.fetch(:class_name)
73
+ pk_attributes = meta.fetch :pk_attributes
74
+
75
+ find_or_initialize object_class, pk_attributes
76
+ end
77
+
78
+ def with_transaction(&block)
79
+ if defined? ActiveRecord
80
+ ActiveRecord::Base.transaction(&block)
81
+ else
82
+ block.call
49
83
  end
50
84
  end
51
85
 
52
- def deserialize_field(object, field, value)
53
- object.send "#{field}=", value # not public_send!
86
+ def load_options(options)
87
+ @serialize_attributes_source = options.fetch :serialize_attributes, @serialize_attributes_source ||
88
+ ->(object) { object.attributes }
89
+
90
+ @initialize_object_source = options.fetch :find_or_initialize, @initialize_object_source ||
91
+ ->(object_class, pk_query) { object_class.respond_to?(:where) && object_class.where(pk_query).first || object_class.new(pk_query) }
92
+
93
+ @persist_object_source = options.fetch :persist, @persist_object_source ||
94
+ ->(object) { object.respond_to?(:save!) && object.save! }
95
+
96
+ @primary_key_fields = options.fetch :primary_key, @primary_key_fields || :id
54
97
  end
55
98
 
56
- def initialize_object(object_handler, data)
57
- id = data.fetch "id"
58
- relation = case object_handler
59
- when String, Symbol then object_handler.to_s.camelize.constantize
60
- else object_handler end
99
+ def primary_key_fields
100
+ Array(@primary_key_fields)
101
+ end
102
+
103
+ def serialize_attributes(*args); serialize_attributes_source.call(*args) end
104
+ def find_or_initialize(*args); initialize_object_source.call(*args) end
105
+ def persist(*args); persist_object_source.call(*args) end
106
+
107
+ def array?(object)
108
+ object.respond_to?(:map) && ! object.is_a?(Hash)
109
+ end
110
+ # ActiveSupport methods
111
+ def symbolize_keys(hash)
112
+ hash.each_with_object({}) do |(key, value), result|
113
+ new_key = key.is_a?(String) ? key.to_sym : key
114
+ new_value = value.is_a?(Hash) ? symbolize_keys(value) : value
115
+
116
+ result[new_key] = new_value
117
+ end
118
+ end
61
119
 
62
- relation.where(id: id).first || relation.new(id: id)
120
+ def constantize(class_name)
121
+ class_name.split('::').inject(Kernel) { |object, name| object = object.const_get(name); object }
63
122
  end
64
123
  end
65
124
  end
@@ -2,7 +2,6 @@ require "spec_helper"
2
2
  require "support/active_record"
3
3
  require "user"
4
4
  require "role"
5
- require "user_serializer"
6
5
 
7
6
  describe Undo::Serializer::ActiveModel do
8
7
  subject { described_class }
@@ -19,17 +18,19 @@ describe Undo::Serializer::ActiveModel do
19
18
  expect(restored_user).to be_persisted
20
19
  end
21
20
 
22
- it "restores object and associations" do
23
- roles = create_list :role, 3, user: user
24
- hash = serializer.serialize user
25
- user.delete
26
- Role.delete_all # HACK for ActiveRecord 3.0
21
+ describe "associations" do
22
+ it "restores provided associations" do
23
+ roles = create_list :role, 3, user: user
24
+ hash = serializer.serialize user, include: :roles
25
+ user.delete
26
+ Role.delete_all # HACK for ActiveRecord 3.0
27
27
 
28
- restored_user = serializer.deserialize hash
28
+ restored_user = serializer.deserialize hash
29
29
 
30
- restored_user.reload # HACK for ActiveRecord 3.0
31
- expect(restored_user).to eq user
32
- expect(restored_user.roles).to eq roles
30
+ restored_user.reload # HACK for ActiveRecord 3.0
31
+ expect(restored_user).to eq user
32
+ expect(restored_user.roles).to eq roles
33
+ end
33
34
  end
34
35
 
35
36
  it "reverts changes to object" do
@@ -43,9 +44,14 @@ describe Undo::Serializer::ActiveModel do
43
44
  expect(restored_user).to eq user.reload
44
45
  end
45
46
 
46
- it "detects default serializer for a model" do
47
- serializer = subject.new
48
- expect(UserSerializer).to receive(:new)
49
- serializer.serialize(user)
47
+ describe "array of objects" do
48
+ it "restores a collection" do
49
+ users = create_list :user, 3
50
+ array = serializer.serialize users, include: :roles
51
+ users.each &:delete
52
+
53
+ restored_users = serializer.deserialize array
54
+ expect(restored_users).to eq users
55
+ end
50
56
  end
51
57
  end
@@ -3,38 +3,70 @@ require "spec_helper"
3
3
  describe Undo::Serializer::ActiveModel do
4
4
  subject { described_class }
5
5
  let(:serializer) { subject.new }
6
- let(:object) { double :object }
7
- let(:am_serializer_method) { :as_json }
6
+ let(:object) { double :object, attributes: { id: 1, foo: "bar", bar: "baz", hello: "world" } }
7
+
8
+ describe "custom finder fields" do
9
+ it "uses finder fields to find the object" do
10
+ FooBarTestObject = Class.new
11
+ serializer = subject.new primary_key: [:foo, :bar]
12
+ allow(object).to receive(:class) { FooBarTestObject }
13
+
14
+ expect(FooBarTestObject).to receive(:new).with(foo: "bar", bar: "baz") { object.as_null_object }
15
+
16
+ hash = serializer.serialize object
17
+ serializer.deserialize hash
18
+ end
19
+ end
8
20
 
9
21
  describe "custom serializer" do
10
- it "uses provided serializer" do
11
- custom_serializer = double :custom_serializer
12
- serializer = subject.new custom_serializer
22
+ it "uses provided attribute serialization" do
23
+ attribute_serializer = double :attribute_serializer
24
+ serializer = subject.new serialize_attributes: attribute_serializer
13
25
 
14
- expect(custom_serializer).to receive am_serializer_method
26
+ expect(attribute_serializer).to receive(:call).with(object)
15
27
  serializer.serialize object
16
28
  end
17
29
 
18
- it "uses custom serializer source" do
19
- custom_serializer_source = double :custom_serializer_source
20
- custom_serializer = double :custom_serializer
21
- serializer = subject.new serializer: custom_serializer_source
30
+ it "uses provided find_or_initialize deserialization" do
31
+ deserializer = double :find_or_initialize_deserializer
32
+ serializer = subject.new find_or_initialize: deserializer
22
33
 
23
- expect(custom_serializer_source).to receive(:call).with(object) { custom_serializer }
24
- expect(custom_serializer).to receive am_serializer_method
25
- serializer.serialize object
34
+ hash = serializer.serialize object
35
+ expect(deserializer).to receive(:call).with(object.class, id: 1) { object.as_null_object }
36
+ serializer.deserialize hash
26
37
  end
27
38
 
28
- it "has lower priority than providing the serializer directly" do
29
- custom_serializer_source = double :custom_serializer_source
30
- custom_serializer = double :custom_serializer
39
+ it "uses provided way of persisting object" do
40
+ persister = double :persister
31
41
 
32
- serializer = subject.new custom_serializer, serializer: custom_serializer_source
42
+ deserializer = double :find_or_initialize_deserializer
43
+ allow(deserializer).to receive(:call) { object.as_null_object }
44
+ serializer = subject.new persist: persister, find_or_initialize: deserializer
33
45
 
34
- expect(custom_serializer_source).not_to receive(:call)
35
- expect(custom_serializer).to receive am_serializer_method
46
+ hash = serializer.serialize object
47
+ expect(persister).to receive(:call).with(object)
48
+ serializer.deserialize hash
49
+ end
50
+ end
36
51
 
37
- serializer.serialize object
52
+ describe "in place serializer options" do
53
+ specify "#serialize" do
54
+ attribute_serializer = double :attribute_serializer
55
+ serializer = subject.new
56
+
57
+ expect(attribute_serializer).to receive(:call).with(object)
58
+ serializer.serialize object,
59
+ serialize_attributes: attribute_serializer
60
+ end
61
+
62
+ specify "#deserialize" do
63
+ deserializer = double :find_or_initialize_deserializer
64
+ serializer = subject.new
65
+
66
+ hash = serializer.serialize object
67
+ expect(deserializer).to receive(:call).with(object.class, id: 1) { object.as_null_object }
68
+ serializer.deserialize hash,
69
+ find_or_initialize: deserializer
38
70
  end
39
71
  end
40
72
 
@@ -7,8 +7,8 @@ Gem::Specification.new do |spec|
7
7
  spec.version = IO.read("VERSION")
8
8
  spec.authors = ["Alexander Paramonov"]
9
9
  spec.email = ["alexander.n.paramonov@gmail.com"]
10
- spec.summary = %q{ActiveModel serializer for Undo gem.}
11
- spec.description = %q{ActiveModel serializer for Undo gem.}
10
+ spec.summary = %q{ActiveModel serializer for Undo gem. Does not require anything from Rails so is friendly to use with POROs.}
11
+ spec.description = %q{ActiveModel serializer for Undo gem. Does not require anything from Rails so is friendly to use with POROs.}
12
12
  spec.homepage = "http://github.com/AlexParamonov/undo-serializer-active_model"
13
13
  spec.license = "MIT"
14
14
 
@@ -17,6 +17,5 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency "activesupport"
21
20
  spec.add_development_dependency 'bundler', '~> 1.0'
22
21
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: undo-serializer-active_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Paramonov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-22 00:00:00.000000000 Z
11
+ date: 2014-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: activesupport
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bundler
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +24,8 @@ dependencies:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
26
  version: '1.0'
41
- description: ActiveModel serializer for Undo gem.
27
+ description: ActiveModel serializer for Undo gem. Does not require anything from Rails
28
+ so is friendly to use with POROs.
42
29
  email:
43
30
  - alexander.n.paramonov@gmail.com
44
31
  executables: []
@@ -68,7 +55,6 @@ files:
68
55
  - spec/support/active_record/role.rb
69
56
  - spec/support/active_record/schema.rb
70
57
  - spec/support/active_record/user.rb
71
- - spec/support/active_record/user_serializer.rb
72
58
  - spec/support/ci_helper.rb
73
59
  - spec/support/factories.rb
74
60
  - spec/undo/serializer/active_model_integration_spec.rb
@@ -94,19 +80,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
80
  version: '0'
95
81
  requirements: []
96
82
  rubyforge_project:
97
- rubygems_version: 2.2.2
83
+ rubygems_version: 2.1.11
98
84
  signing_key:
99
85
  specification_version: 4
100
- summary: ActiveModel serializer for Undo gem.
86
+ summary: ActiveModel serializer for Undo gem. Does not require anything from Rails
87
+ so is friendly to use with POROs.
101
88
  test_files:
102
89
  - spec/spec_helper.rb
103
90
  - spec/support/active_record.rb
104
91
  - spec/support/active_record/role.rb
105
92
  - spec/support/active_record/schema.rb
106
93
  - spec/support/active_record/user.rb
107
- - spec/support/active_record/user_serializer.rb
108
94
  - spec/support/ci_helper.rb
109
95
  - spec/support/factories.rb
110
96
  - spec/undo/serializer/active_model_integration_spec.rb
111
97
  - spec/undo/serializer/active_model_spec.rb
112
- has_rdoc:
@@ -1,6 +0,0 @@
1
- require "active_model_serializers"
2
-
3
- class UserSerializer < ActiveModel::Serializer
4
- attributes :id, :name, :email
5
- has_many :roles, key: :has_many___roles
6
- end