surrealist 0.0.4 → 0.0.7

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: e161583b21b67122b16fc80818d02a053076fb85
4
- data.tar.gz: fb10c25384a75bbf3b8c68454086dd9d6e145093
3
+ metadata.gz: 7b1db1906a694049f0d30a384b863248a565fa98
4
+ data.tar.gz: 249561f2fe8bb08ca93fd04c9a48fbc111b4d5f3
5
5
  SHA512:
6
- metadata.gz: 79f775182748e9f4b22b6d88bf05f4fb53f35afa177a85c3290097ab2923df74538e869d7d2b15494e24415da8d08f64de3d21b45b05821148cef8fa9bdeefd1
7
- data.tar.gz: 0eaa71f001668e89373fc347caa8d1ff70a43a5f780bdedafbc62d39bf344c1e2c9b3c67bf106b0ba6e052bdf988597b59f9f4da7b4f688f63058a8ec50a13fb
6
+ metadata.gz: a89eaf9e476152176d26c25f9475606a7372abb07a1295cb9eb51192934973673800ff116af6e6a3591ca2d7785c60b8a635cb2062c7a339e503c253da96c732
7
+ data.tar.gz: 2bed8315135646784faf8b3dc60c186654e136cc6c1416f65a00aed3753c0943094cef7eff0168f5c6feeeb5833a931689189a76d18a031605301cf27d926e9b
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  .ruby-version
11
11
  TODO.md
12
+ *.gem
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # 0.0.6
2
+ * Add `build_schema` instance method that builds hash from the schema without serializing it to json.
3
+ * Allow nil values by default
4
+ * Allow nested objects
data/Gemfile CHANGED
@@ -4,5 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'coveralls', require: false
7
- gem 'rubocop-config-umbrellio', require: false
8
7
  gem 'yard', require: false unless ENV['TRAVIS']
data/README.md CHANGED
@@ -1,14 +1,21 @@
1
1
  # Surrealist
2
- [![Build Status](https://travis-ci.com/nesaulov/surrealist.svg?token=UM7SJoXQ7Y56NHBpib7v&branch=master)](https://travis-ci.com/nesaulov/surrealist)
2
+ [![Build Status](https://travis-ci.org/nesaulov/surrealist.svg?branch=master)](https://travis-ci.org/nesaulov/surrealist)
3
3
  [![Coverage Status](https://coveralls.io/repos/github/nesaulov/surrealist/badge.svg?branch=master)](https://coveralls.io/github/nesaulov/surrealist?branch=master)
4
+ [![Inline docs](http://inch-ci.org/github/nesaulov/surrealist.svg?branch=master)](http://inch-ci.org/github/nesaulov/surrealist)
5
+ [![Gem Version](https://badge.fury.io/rb/surrealist.svg)](https://rubygems.org/gems/surrealist)
4
6
 
5
7
  A gem that provides DSL for serialization of plain old Ruby objects to JSON in a declarative style
6
8
  by defining a `schema`. It also provides a trivial type checking in the runtime before serialization.
7
9
  [Yard documentation](http://www.rubydoc.info/github/nesaulov/surrealist/master)
8
10
 
11
+ ## Current status
12
+ In development, not yet ready for real projects.
13
+
9
14
  ## Motivation
10
- A typical use case for this gem could be, for example, serializing a decorated object outside of the view context.
11
-
15
+ A typical use case for this gem could be, for example, serializing a (decorated) object outside
16
+ of the view context. The schema is described through a hash, so you can build the structure
17
+ of serialized object independently of its methods and attributes.
18
+
12
19
  ## Installation
13
20
 
14
21
  Add this line to your application's Gemfile:
@@ -36,37 +43,41 @@ that will be used for type-checks.
36
43
  ### Simple example
37
44
  * Include Surrealist in your class.
38
45
  * Define a schema with methods that need to be serialized.
46
+
39
47
  ``` ruby
40
48
  class Person
41
49
  include Surrealist
42
-
50
+
43
51
  schema do
44
52
  {
45
53
  foo: String,
46
54
  bar: Integer,
47
- }
55
+ }
48
56
  end
49
-
57
+
50
58
  def foo
51
59
  'This is a string'
52
60
  end
53
-
61
+
54
62
  def bar
55
63
  42
56
64
  end
57
65
  end
58
66
  ```
67
+
59
68
  * Surrealize it.
69
+
60
70
  ``` ruby
61
71
  Person.new.surrealize
62
72
  # => "{\"foo\":\"This is a string\",\"bar\":42}"
63
73
  ```
64
74
 
65
75
  ### Nested structures
76
+
66
77
  ``` ruby
67
78
  class Person
68
79
  include Surrealist
69
-
80
+
70
81
  schema do
71
82
  {
72
83
  foo: String,
@@ -81,22 +92,23 @@ class Person
81
92
  end
82
93
  # ... method definitions
83
94
  end
84
-
95
+
85
96
  Person.find_by(email: 'example@email.com').surrealize
86
97
  # => "{\"foo\":\"Some string\",\"name\":\"John Doe\",\"nested\":{\"at\":{\"any\":42,\"level\":true}}}"
87
98
  ```
88
99
 
89
100
  ### Type Errors
101
+
90
102
  `Surrealist::InvalidTypeError` is thrown if types mismatch.
91
103
 
92
104
  ``` ruby
93
105
  class CreditCard
94
106
  include Surrealist
95
-
107
+
96
108
  schema do
97
109
  { number: Integer }
98
110
  end
99
-
111
+
100
112
  def number; 'string'; end
101
113
  end
102
114
 
@@ -105,12 +117,14 @@ CreditCard.new.surrealize
105
117
  ```
106
118
 
107
119
  ### Undefined methods in schema
120
+
108
121
  `Surrealist::UndefinedMethodError` is thrown if a key defined in the schema does not have
109
122
  a corresponding method defined in the object.
123
+
110
124
  ``` ruby
111
125
  class Car
112
126
  include Surrealist
113
-
127
+
114
128
  schema do
115
129
  { weight: Integer }
116
130
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A module for boolean type-checks.
4
+ module Boolean; end
@@ -3,37 +3,125 @@
3
3
  module Surrealist
4
4
  # A class that builds a hash from the schema and type-checks the values.
5
5
  class Builder
6
- # A method that goes recursively through the schema hash, defines the values and type-checks them.
7
- #
8
- # @param [Hash] schema the schema defined in the object's class.
9
- #
10
- # @param [Object] instance the instance of the object which methods from the schema are called on.
11
- #
12
- # @raise +Surrealist::InvalidTypeError+ if type-check failed at some point.
13
- #
14
- # @raise +Surrealist::UndefinedMethodError+ if a key defined in the schema
15
- # does not have a corresponding method on the object.
16
- #
17
- # @return [Hash] a hash that will be dumped into JSON.
18
- def self.call(schema, instance)
19
- schema.each do |key, value|
20
- if value.is_a? Hash
21
- call(value, instance)
6
+ class << self
7
+ # A method that goes recursively through the schema hash, defines the values and type-checks them.
8
+ #
9
+ # @param [Hash] schema the schema defined in the object's class.
10
+ # @param [Object] instance the instance of the object which methods from the schema are called on.
11
+ #
12
+ # @raise +Surrealist::UndefinedMethodError+ if a key defined in the schema
13
+ # does not have a corresponding method on the object.
14
+ #
15
+ # @return [Hash] a hash that will be dumped into JSON.
16
+ def call(schema:, instance:)
17
+ schema.each do |key, value|
18
+ if value.is_a?(Hash)
19
+ parse_hash(hash: value, schema: schema, instance: instance, key: key)
20
+ else
21
+ type = value
22
+ value = instance.is_a?(Hash) ? instance[key] : instance.send(key)
23
+ assign_value(method: key, value: value, type: type) { schema[key] = value }
24
+ end
25
+ end
26
+ rescue NoMethodError => e
27
+ raise Surrealist::UndefinedMethodError,
28
+ "#{e.message}. You have probably defined a key " \
29
+ "in the schema that doesn't have a corresponding method."
30
+ end
31
+
32
+ private
33
+
34
+ # Checks if hash represents methods on the instance.
35
+ #
36
+ # @param [Hash] hash a value from the schema hash.
37
+ # @param [Hash] schema the schema defined in the object's class.
38
+ # @param [Object] instance the instance of the object which methods from the schema are called on.
39
+ # @param [Symbol] key a key from the schema hash.
40
+ #
41
+ # @return [Hash] schema
42
+ def parse_hash(hash:, schema:, instance:, key:)
43
+ if instance.respond_to?(key)
44
+ maybe_take_values_from_instance(instance: instance, method: key, hash: hash, schema: schema)
22
45
  else
23
- val = instance.send(key)
46
+ call(schema: hash, instance: instance)
47
+ end
48
+ end
49
+
50
+ # Checks if object's method include schema keys.
51
+ #
52
+ # @param [Object] instance the instance of the object which methods from the schema are called on.
53
+ # @param [Symbol] method a key from the schema hash representing a method on the instance.
54
+ # @param [Hash] hash a value from the schema hash.
55
+ # @param [Hash] schema the schema defined in the object's class.
56
+ #
57
+ # @return [Hash] schema
58
+ def maybe_take_values_from_instance(instance:, method:, hash:, schema:)
59
+ object = instance.send(method)
24
60
 
25
- if val.is_a? value
26
- schema[key] = val
61
+ hash.each do |key, value|
62
+ if object.methods.include?(key)
63
+ take_values_from_instance(instance: object, value: value, hash: hash, key: key,
64
+ schema: schema, method: method)
27
65
  else
28
- raise Surrealist::InvalidTypeError,
29
- "Wrong type for key `#{key}`. Expected #{value}, got #{val.class}."
66
+ call(schema: hash, instance: object)
30
67
  end
31
68
  end
32
69
  end
33
- rescue NoMethodError => e
34
- raise Surrealist::UndefinedMethodError,
35
- "#{e.message}. You have probably defined a key " \
36
- "in the schema that doesn't have a corresponding method."
70
+
71
+ # Invokes methods on the instance and puts return values into the schema hash.
72
+ #
73
+ # @param [Object] instance the instance of the object which methods from the schema are called on.
74
+ # @param [Class | Hash] value either type of value or a hash.
75
+ # @param [Hash] hash a value from the schema hash.
76
+ # @param [Hash] schema the schema defined in the object's class.
77
+ # @param [Symbol] method a key from the schema hash representing a method on the instance.
78
+ #
79
+ # @return [Hash] schema
80
+ def take_values_from_instance(instance:, value:, hash:, key:, schema:, method:)
81
+ result = instance.send(key)
82
+
83
+ if value.is_a?(Hash)
84
+ parse_hash(hash: value, schema: hash, instance: result, key: key)
85
+ else
86
+ type = value
87
+ assign_value(method: key, value: result, type: type) do
88
+ schema[method] = schema[method].merge(key => result)
89
+ end
90
+ end
91
+ end
92
+
93
+ # Assigns value returned from a method to a corresponding key in the schema hash.
94
+ #
95
+ # @param [Symbol] method a key from the schema hash representing a method on the instance.
96
+ # @param [Object] value a value that has to be type-checked.
97
+ # @param [Class] type class representing data type.
98
+ #
99
+ # @raise +Surrealist::InvalidTypeError+ if type-check failed at some point.
100
+ #
101
+ # @return [Hash] schema
102
+ def assign_value(method:, value:, type:, &_block)
103
+ if type_check_passed?(value: value, type: type)
104
+ yield if block_given?
105
+ else
106
+ raise Surrealist::InvalidTypeError,
107
+ "Wrong type for key `#{method}`. Expected #{type}, got #{value.class}."
108
+ end
109
+ end
110
+
111
+ # Checks if value returned from a method is an instance of type class specified
112
+ # in schema or NilClass.
113
+ #
114
+ # @param [any] value value returned from a method.
115
+ # @param [Class] type class representing data type.
116
+ #
117
+ # @return [boolean]
118
+ def type_check_passed?(value:, type:)
119
+ if type == Boolean
120
+ [true, false].include?(value)
121
+ else
122
+ value.nil? || value.is_a?(type)
123
+ end
124
+ end
37
125
  end
38
126
  end
39
127
  end
@@ -7,5 +7,10 @@ module Surrealist
7
7
  def surrealize
8
8
  Surrealist.surrealize(self)
9
9
  end
10
+
11
+ # Invokes +Surrealist+'s class method +build_schema+
12
+ def build_schema
13
+ Surrealist.build_schema(self)
14
+ end
10
15
  end
11
16
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Surrealist
4
4
  # Defines the version of Surrealist
5
- VERSION = '0.0.4'
5
+ VERSION = '0.0.7'
6
6
  end
data/lib/surrealist.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'surrealist/class_methods'
4
4
  require_relative 'surrealist/instance_methods'
5
- require_relative 'surrealist/extensions/boolean'
5
+ require_relative 'surrealist/boolean'
6
6
  require 'multi_json'
7
7
 
8
8
  # Main module that provides the +schema+ class method and +surrealize+ instance method.
@@ -30,7 +30,6 @@ module Surrealist
30
30
  #
31
31
  # @param [Object] instance of a class that has +Surrealist+ included.
32
32
  #
33
- #
34
33
  # @return [String] a json-formatted string corresponding to the schema
35
34
  # provided in the object's class. Values will be taken from the return values
36
35
  # of appropriate methods from the object.
@@ -48,25 +47,72 @@ module Surrealist
48
47
  #
49
48
  # schema do
50
49
  # {
51
- # foo: String,
52
- # bar: Integer,
50
+ # name: String,
51
+ # age: Integer,
53
52
  # }
54
53
  # end
55
54
  #
56
- # def foo; 'A string'; end
57
- # def bar; 42; end
55
+ # def name
56
+ # 'Nikita'
57
+ # end
58
+ #
59
+ # def age
60
+ # 23
61
+ # end
58
62
  # end
59
63
  #
60
64
  # User.new.surrealize
61
- # # => "{\"foo\":\"A string\",\"bar\":42}"
65
+ # # => "{\"name\":\"Nikita\",\"age\":23}"
62
66
  # # For more examples see README
63
67
  def self.surrealize(instance)
68
+ ::MultiJson.dump(build_schema(instance))
69
+ end
70
+
71
+ # Builds hash from schema provided in the object's class and type-checks the values.
72
+ #
73
+ # @param [Object] instance of a class that has +Surrealist+ included.
74
+ #
75
+ # @return [Hash] a hash corresponding to the schema
76
+ # provided in the object's class. Values will be taken from the return values
77
+ # of appropriate methods from the object.
78
+ #
79
+ # @raise +Surrealist::UnknownSchemaError+ if no schema was provided in the object's class.
80
+ #
81
+ # @raise +Surrealist::InvalidTypeError+ if type-check failed at some point.
82
+ #
83
+ # @raise +Surrealist::UndefinedMethodError+ if a key defined in the schema
84
+ # does not have a corresponding method on the object.
85
+ #
86
+ # @example Define a schema and surrealize the object
87
+ # class User
88
+ # include Surrealist
89
+ #
90
+ # schema do
91
+ # {
92
+ # name: String,
93
+ # age: Integer,
94
+ # }
95
+ # end
96
+ #
97
+ # def name
98
+ # 'Nikita'
99
+ # end
100
+ #
101
+ # def age
102
+ # 23
103
+ # end
104
+ # end
105
+ #
106
+ # User.new.build_schema
107
+ # # => { name: 'Nikita', age: 23 }
108
+ # # For more examples see README
109
+ def self.build_schema(instance)
64
110
  schema = instance.__surrealist_schema rescue nil
65
111
 
66
112
  if schema.nil?
67
113
  raise Surrealist::UnknownSchemaError, "Can't serialize #{instance.class} - no schema was provided."
68
114
  end
69
115
 
70
- ::MultiJson.dump(Builder.call(schema, instance))
116
+ Builder.call(schema: schema, instance: instance)
71
117
  end
72
118
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: surrealist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Esaulov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-07 00:00:00.000000000 Z
11
+ date: 2017-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -91,16 +91,16 @@ extra_rdoc_files: []
91
91
  files:
92
92
  - ".gitignore"
93
93
  - ".rspec"
94
- - ".rubocop.yml"
95
94
  - ".travis.yml"
95
+ - CHANGELOG.md
96
96
  - Gemfile
97
97
  - LICENSE.txt
98
98
  - README.md
99
99
  - bin/console
100
100
  - lib/surrealist.rb
101
+ - lib/surrealist/boolean.rb
101
102
  - lib/surrealist/builder.rb
102
103
  - lib/surrealist/class_methods.rb
103
- - lib/surrealist/extensions/boolean.rb
104
104
  - lib/surrealist/instance_methods.rb
105
105
  - lib/surrealist/schema_definer.rb
106
106
  - lib/surrealist/version.rb
data/.rubocop.yml DELETED
@@ -1,13 +0,0 @@
1
- inherit_gem:
2
- rubocop-config-umbrellio: lib/rubocop.yml
3
-
4
- AllCops:
5
- TargetRubyVersion: 2.4
6
-
7
- Style/StringLiterals:
8
- EnforcedStyle: single_quotes
9
- ConsistentQuotesInMultiline: true
10
- Metrics/LineLength:
11
- Max: 106
12
- Style/SingleLineMethods:
13
- Enabled: false
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # A module that is included in +TrueClass+ and +FalseClass+ for boolean type-checks.
4
- module Boolean; end
5
- # TrueClass monkey-patch.
6
- class TrueClass; include Boolean; end
7
- # FalseClass monkey-patch.
8
- class FalseClass; include Boolean; end