jsonapionify 0.0.1.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +29 -0
- data/.csslintrc +2 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +1171 -0
- data/.ruby-version +1 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/Guardfile +14 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +34 -0
- data/TODO +13 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/config.ru +15 -0
- data/fixtures/documentation.json +364 -0
- data/jsonapionify.gemspec +50 -0
- data/lib/core_ext/boolean.rb +3 -0
- data/lib/jsonapionify/api/action.rb +211 -0
- data/lib/jsonapionify/api/attribute.rb +67 -0
- data/lib/jsonapionify/api/base/app_builder.rb +33 -0
- data/lib/jsonapionify/api/base/class_methods.rb +73 -0
- data/lib/jsonapionify/api/base/delegation.rb +15 -0
- data/lib/jsonapionify/api/base/doc_helper.rb +47 -0
- data/lib/jsonapionify/api/base/reloader.rb +10 -0
- data/lib/jsonapionify/api/base/resource_definitions.rb +39 -0
- data/lib/jsonapionify/api/base.rb +25 -0
- data/lib/jsonapionify/api/context.rb +14 -0
- data/lib/jsonapionify/api/context_delegate.rb +42 -0
- data/lib/jsonapionify/api/errors.rb +6 -0
- data/lib/jsonapionify/api/errors_object.rb +66 -0
- data/lib/jsonapionify/api/header_options.rb +13 -0
- data/lib/jsonapionify/api/param_options.rb +46 -0
- data/lib/jsonapionify/api/relationship/blocks.rb +41 -0
- data/lib/jsonapionify/api/relationship/many.rb +61 -0
- data/lib/jsonapionify/api/relationship/one.rb +36 -0
- data/lib/jsonapionify/api/relationship.rb +89 -0
- data/lib/jsonapionify/api/resource/builders.rb +81 -0
- data/lib/jsonapionify/api/resource/class_methods.rb +82 -0
- data/lib/jsonapionify/api/resource/defaults/actions.rb +11 -0
- data/lib/jsonapionify/api/resource/defaults/errors.rb +99 -0
- data/lib/jsonapionify/api/resource/defaults/request_contexts.rb +96 -0
- data/lib/jsonapionify/api/resource/defaults/response_contexts.rb +31 -0
- data/lib/jsonapionify/api/resource/defaults.rb +10 -0
- data/lib/jsonapionify/api/resource/definitions/actions.rb +196 -0
- data/lib/jsonapionify/api/resource/definitions/attributes.rb +51 -0
- data/lib/jsonapionify/api/resource/definitions/contexts.rb +16 -0
- data/lib/jsonapionify/api/resource/definitions/helpers.rb +9 -0
- data/lib/jsonapionify/api/resource/definitions/pagination.rb +79 -0
- data/lib/jsonapionify/api/resource/definitions/params.rb +49 -0
- data/lib/jsonapionify/api/resource/definitions/relationships.rb +42 -0
- data/lib/jsonapionify/api/resource/definitions/request_headers.rb +103 -0
- data/lib/jsonapionify/api/resource/definitions/response_headers.rb +22 -0
- data/lib/jsonapionify/api/resource/definitions/scopes.rb +50 -0
- data/lib/jsonapionify/api/resource/definitions/sorting.rb +85 -0
- data/lib/jsonapionify/api/resource/definitions.rb +14 -0
- data/lib/jsonapionify/api/resource/error_handling.rb +108 -0
- data/lib/jsonapionify/api/resource/http.rb +11 -0
- data/lib/jsonapionify/api/resource/includer.rb +4 -0
- data/lib/jsonapionify/api/resource.rb +35 -0
- data/lib/jsonapionify/api/response.rb +47 -0
- data/lib/jsonapionify/api/server/mock_response.rb +37 -0
- data/lib/jsonapionify/api/server/request.rb +78 -0
- data/lib/jsonapionify/api/server.rb +50 -0
- data/lib/jsonapionify/api/test_helper.rb +52 -0
- data/lib/jsonapionify/api.rb +9 -0
- data/lib/jsonapionify/autoload.rb +52 -0
- data/lib/jsonapionify/callbacks.rb +49 -0
- data/lib/jsonapionify/character_range.rb +41 -0
- data/lib/jsonapionify/continuation.rb +26 -0
- data/lib/jsonapionify/documentation/template.erb +487 -0
- data/lib/jsonapionify/documentation.rb +40 -0
- data/lib/jsonapionify/enumerable_observer.rb +91 -0
- data/lib/jsonapionify/indented_string.rb +27 -0
- data/lib/jsonapionify/inherited_attributes.rb +125 -0
- data/lib/jsonapionify/structure/collections/base.rb +104 -0
- data/lib/jsonapionify/structure/collections/errors.rb +7 -0
- data/lib/jsonapionify/structure/collections/included_resources.rb +39 -0
- data/lib/jsonapionify/structure/collections/resource_identifiers.rb +7 -0
- data/lib/jsonapionify/structure/collections/resources.rb +7 -0
- data/lib/jsonapionify/structure/helpers/errors.rb +71 -0
- data/lib/jsonapionify/structure/helpers/inherits_origin.rb +17 -0
- data/lib/jsonapionify/structure/helpers/member_names.rb +37 -0
- data/lib/jsonapionify/structure/helpers/meta_delegate.rb +16 -0
- data/lib/jsonapionify/structure/helpers/object_defaults.rb +123 -0
- data/lib/jsonapionify/structure/helpers/object_setters.rb +21 -0
- data/lib/jsonapionify/structure/helpers/pagination_links.rb +10 -0
- data/lib/jsonapionify/structure/helpers/validations.rb +296 -0
- data/lib/jsonapionify/structure/maps/base.rb +25 -0
- data/lib/jsonapionify/structure/maps/error_links.rb +7 -0
- data/lib/jsonapionify/structure/maps/links.rb +21 -0
- data/lib/jsonapionify/structure/maps/relationship_links.rb +11 -0
- data/lib/jsonapionify/structure/maps/relationships.rb +23 -0
- data/lib/jsonapionify/structure/maps/resource_links.rb +7 -0
- data/lib/jsonapionify/structure/maps/top_level_links.rb +10 -0
- data/lib/jsonapionify/structure/objects/attributes.rb +29 -0
- data/lib/jsonapionify/structure/objects/base.rb +166 -0
- data/lib/jsonapionify/structure/objects/error.rb +16 -0
- data/lib/jsonapionify/structure/objects/included_resource.rb +14 -0
- data/lib/jsonapionify/structure/objects/jsonapi.rb +7 -0
- data/lib/jsonapionify/structure/objects/link.rb +18 -0
- data/lib/jsonapionify/structure/objects/meta.rb +7 -0
- data/lib/jsonapionify/structure/objects/relationship.rb +20 -0
- data/lib/jsonapionify/structure/objects/resource.rb +45 -0
- data/lib/jsonapionify/structure/objects/resource_identifier.rb +40 -0
- data/lib/jsonapionify/structure/objects/source.rb +10 -0
- data/lib/jsonapionify/structure/objects/top_level.rb +105 -0
- data/lib/jsonapionify/structure.rb +27 -0
- data/lib/jsonapionify/types/array_type.rb +32 -0
- data/lib/jsonapionify/types/boolean_type.rb +22 -0
- data/lib/jsonapionify/types/date_string_type.rb +28 -0
- data/lib/jsonapionify/types/float_type.rb +8 -0
- data/lib/jsonapionify/types/integer_type.rb +9 -0
- data/lib/jsonapionify/types/object_type.rb +22 -0
- data/lib/jsonapionify/types/string_type.rb +66 -0
- data/lib/jsonapionify/types/time_string_type.rb +28 -0
- data/lib/jsonapionify/types.rb +49 -0
- data/lib/jsonapionify/unstrict_proc.rb +28 -0
- data/lib/jsonapionify/version.rb +3 -0
- data/lib/jsonapionify.rb +37 -0
- metadata +530 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module JSONAPIonify::Structure
|
2
|
+
module Objects
|
3
|
+
# ResourceObjects appear in a JSON API document to represent resources.
|
4
|
+
class Resource < ResourceIdentifier
|
5
|
+
# The `id` member is not required when the resource object originates at the
|
6
|
+
# client and represents a new resource to be created on the server.
|
7
|
+
must_contain! :id, if: ->(obj) { obj.server? } # an id representing the resource
|
8
|
+
|
9
|
+
# In addition, a resource object **MAY** contain any of these top-level members:
|
10
|
+
may_contain! :attributes, # An AttributesObject representing some of the resource's data.
|
11
|
+
:relationships, # A RelationshipsObject describing relationships between the resource and other JSON API resources.
|
12
|
+
:links, # A LinksObject containing links related to the resource.
|
13
|
+
:meta # A MetaObject containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.
|
14
|
+
|
15
|
+
implements :attributes, as: Attributes
|
16
|
+
implements :relationships, as: Maps::Relationships
|
17
|
+
implements :links, as: Maps::Links
|
18
|
+
implements :meta, as: Meta
|
19
|
+
|
20
|
+
# Note: This spec is agnostic about inflection rules, so the value of `type`
|
21
|
+
# can be either plural or singular. However, the same value should be used
|
22
|
+
# consistently throughout an implementation.
|
23
|
+
def attribute_keys
|
24
|
+
return [] unless self[:attributes]
|
25
|
+
self[:attributes].keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def relationship_keys
|
29
|
+
return [] unless self[:relationships]
|
30
|
+
self[:relationships].keys
|
31
|
+
end
|
32
|
+
|
33
|
+
def relates_to?(other)
|
34
|
+
relationships = self[:relationships]
|
35
|
+
return false unless relationships
|
36
|
+
relationships.any? do |_, resource_identifier|
|
37
|
+
Array.wrap(resource_identifier[:data]).any? do |rel|
|
38
|
+
rel[:id] == other[:id] && rel[:type] == other[:type]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module JSONAPIonify::Structure
|
2
|
+
module Objects
|
3
|
+
class ResourceIdentifier < Base
|
4
|
+
# A resource object **MUST** contain at least the following top-level members:
|
5
|
+
must_contain! :id, :type # Describes ResourceObjects that share common attributes and relationships.
|
6
|
+
|
7
|
+
# Identification
|
8
|
+
# ==============
|
9
|
+
#
|
10
|
+
# The values of the `id` and `type` members **MUST** be strings.
|
11
|
+
type_of! :id, must_be: String
|
12
|
+
type_of! :type, must_be: String
|
13
|
+
|
14
|
+
# The values of `type` members **MUST** adhere to the same constraints as member names.
|
15
|
+
validate!(:type, message: 'is not a valid member name') do |*, value|
|
16
|
+
Helpers::MemberNames.valid? value
|
17
|
+
end
|
18
|
+
|
19
|
+
validate_object!(with: :duplicate_does_not_exist?, message: 'is not unique')
|
20
|
+
|
21
|
+
def duplicate_exists?
|
22
|
+
return false unless parent.is_a?(Array)
|
23
|
+
peers = parent - [self]
|
24
|
+
peers.any? { |peer| same_as? peer }
|
25
|
+
end
|
26
|
+
|
27
|
+
def duplicate_does_not_exist?
|
28
|
+
!duplicate_exists?
|
29
|
+
end
|
30
|
+
|
31
|
+
def same_as?(other)
|
32
|
+
return false unless other.is_a? ResourceIdentifier
|
33
|
+
matches_id = other.has_key?(:id) && has_key?(:id) && other[:id] == self[:id]
|
34
|
+
matches_type = other[:type] == self[:type]
|
35
|
+
matches_type && matches_id
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module JSONAPIonify::Structure
|
2
|
+
module Objects
|
3
|
+
class Source < Base
|
4
|
+
may_contain!(
|
5
|
+
:pointer, # a JSON Pointer [[RFC6901](https://tools.ietf.org/html/rfc6901)] to the associated entity in the request document
|
6
|
+
:parameter # a string indicating which URI query parameter caused the error.
|
7
|
+
)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# TopLevelObject
|
2
|
+
# ========
|
3
|
+
#
|
4
|
+
# A JSON object **MUST** be at the root of every JSON API request and response
|
5
|
+
# containing data. This object defines a document's "top level".
|
6
|
+
module JSONAPIonify::Structure
|
7
|
+
module Objects
|
8
|
+
class TopLevel < Base
|
9
|
+
attr_reader :origin
|
10
|
+
|
11
|
+
default(:jsonapi) { Jsonapi.new version: '1.0' }
|
12
|
+
|
13
|
+
# A document **MUST** contain at least one of:
|
14
|
+
must_contain_one_of!(
|
15
|
+
# **data:** The document's "primary data"
|
16
|
+
:data,
|
17
|
+
# **errors:** An array of errors
|
18
|
+
:errors,
|
19
|
+
# **meta:** a meta object that contains non-standard meta-information.
|
20
|
+
:meta
|
21
|
+
)
|
22
|
+
|
23
|
+
collects :errors, as: Collections::Errors
|
24
|
+
implements :meta, as: Meta
|
25
|
+
|
26
|
+
# The members `data` and `errors` **MUST NOT** coexist in the same document.
|
27
|
+
must_not_coexist! :data, :errors
|
28
|
+
|
29
|
+
# A document **MAY** contain any of these top-level members:
|
30
|
+
may_contain!(
|
31
|
+
# **links:** a links_object related to the primary data.
|
32
|
+
:links,
|
33
|
+
# **included:** an array of resource objects that are related to the primary.
|
34
|
+
:included,
|
35
|
+
# **jsonapi:** an object describing the server's implementation.
|
36
|
+
:jsonapi
|
37
|
+
)
|
38
|
+
|
39
|
+
implements :links, as: Maps::TopLevelLinks
|
40
|
+
collects :included, as: Collections::IncludedResources
|
41
|
+
implements :jsonapi, as: Jsonapi
|
42
|
+
is_resource = ->(obj) { (obj.keys - %i{id type}).present? }
|
43
|
+
collects_or_implements(
|
44
|
+
:data,
|
45
|
+
collects: Collections::Resources,
|
46
|
+
implements: Resource,
|
47
|
+
if: ->(obj) {
|
48
|
+
obj.is_a?(Resource) || (obj.is_a?(Hash) && is_resource[obj])
|
49
|
+
})
|
50
|
+
collects_or_implements(
|
51
|
+
:data,
|
52
|
+
collects: Collections::ResourceIdentifiers,
|
53
|
+
implements: ResourceIdentifier,
|
54
|
+
if: ->(obj) {
|
55
|
+
obj.is_a?(ResourceIdentifier) || (obj.is_a?(Hash) && !is_resource[obj])
|
56
|
+
})
|
57
|
+
|
58
|
+
# If a document does not contain a top-level `data` key, the `included` member
|
59
|
+
# **MUST NOT** be present either.
|
60
|
+
may_not_exist! :included, without: :data
|
61
|
+
|
62
|
+
# The document's "primary data" is a representation of the resource or collection
|
63
|
+
# of resources targeted by a request.
|
64
|
+
# Primary data **MUST** be either:
|
65
|
+
type_of! :data, must_be: [
|
66
|
+
# A single **ResourceObject**
|
67
|
+
Resource,
|
68
|
+
# A single **ResourceIdentifierObject**
|
69
|
+
ResourceIdentifier,
|
70
|
+
# Null
|
71
|
+
NilClass,
|
72
|
+
# A collection of **ResourceObjects**
|
73
|
+
Collections::Resources,
|
74
|
+
# A collection of **[ResourceIdentifierObjects](resource_identifier_object.html)**
|
75
|
+
Collections::ResourceIdentifiers
|
76
|
+
]
|
77
|
+
|
78
|
+
def compile(*)
|
79
|
+
compiled = super
|
80
|
+
compiled_errors = compiled['errors'] || []
|
81
|
+
all_errors = compiled_errors | errors.as_collection.compile
|
82
|
+
if all_errors.present?
|
83
|
+
self.class.new(
|
84
|
+
errors: all_errors,
|
85
|
+
meta: {
|
86
|
+
invalid_object: to_hash
|
87
|
+
}
|
88
|
+
).compile
|
89
|
+
else
|
90
|
+
compiled
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def as(origin)
|
95
|
+
copy.tap do |obj|
|
96
|
+
obj.instance_variable_set :@origin, origin
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
after_initialize do
|
101
|
+
@origin = :server
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module JSONAPIonify
|
2
|
+
module Structure
|
3
|
+
|
4
|
+
ValidationError = Class.new StandardError
|
5
|
+
|
6
|
+
module Collections
|
7
|
+
extend JSONAPIonify::Autoload
|
8
|
+
autoload_all 'structure/collections'
|
9
|
+
end
|
10
|
+
|
11
|
+
module Maps
|
12
|
+
extend JSONAPIonify::Autoload
|
13
|
+
autoload_all 'structure/maps'
|
14
|
+
end
|
15
|
+
|
16
|
+
module Objects
|
17
|
+
include Maps
|
18
|
+
extend JSONAPIonify::Autoload
|
19
|
+
autoload_all 'structure/objects'
|
20
|
+
end
|
21
|
+
|
22
|
+
module Helpers
|
23
|
+
extend JSONAPIonify::Autoload
|
24
|
+
autoload_all 'structure/helpers'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module JSONAPIonify::Types
|
2
|
+
class ArrayType < BaseType
|
3
|
+
|
4
|
+
after_initialize do
|
5
|
+
unless options[:of].is_a? BaseType
|
6
|
+
raise TypeError, "#{options[:of]} is not a valid JSON type."
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def load(value)
|
11
|
+
super unless options[:of]
|
12
|
+
value.map do |item|
|
13
|
+
options[:of].load(item)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def dump(value)
|
18
|
+
super unless options[:of]
|
19
|
+
value.map do |item|
|
20
|
+
options[:of].dump(item)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def sample(field_name)
|
25
|
+
field_name = field_name.to_s.singularize.to_sym
|
26
|
+
3.times.map do
|
27
|
+
(options[:of] || StringType.new).sample(field_name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module JSONAPIonify::Types
|
2
|
+
class BooleanType < BaseType
|
3
|
+
|
4
|
+
def load(value)
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
def dump(value)
|
9
|
+
case value
|
10
|
+
when true, false
|
11
|
+
value
|
12
|
+
else
|
13
|
+
raise TypeError, "#{value} is not a valid JSON #{name}."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def sample(*)
|
18
|
+
[true, false].sample
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'faker'
|
2
|
+
|
3
|
+
module JSONAPIonify::Types
|
4
|
+
class DateStringType < BaseType
|
5
|
+
def load(value)
|
6
|
+
Date.parse value
|
7
|
+
end
|
8
|
+
|
9
|
+
def dump(value)
|
10
|
+
case value
|
11
|
+
when Date
|
12
|
+
Oj.dump(value.to_date)
|
13
|
+
else
|
14
|
+
raise TypeError, "#{value} is not a valid JSON #{name}."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def sample(field_name)
|
19
|
+
field_name = field_name.to_s
|
20
|
+
if field_name.to_s.end_with?('ed_at') || field_name.include?('start')
|
21
|
+
Faker::Date.backward
|
22
|
+
elsif field_name.include?('end')
|
23
|
+
Faker::Date.forward
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
|
3
|
+
module JSONAPIonify::Types
|
4
|
+
class ObjectType < BaseType
|
5
|
+
|
6
|
+
def load(value)
|
7
|
+
super(value).deep_symbolize_keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def dump(value)
|
11
|
+
super(value.deep_stringify_keys)
|
12
|
+
end
|
13
|
+
|
14
|
+
def sample(field_name)
|
15
|
+
field_name = field_name.to_s.singularize.to_sym
|
16
|
+
%i{foo bar baz}.each_with_object({}) do |k, h|
|
17
|
+
h[k] = StringType.new.sample(field_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'faker'
|
2
|
+
|
3
|
+
module JSONAPIonify::Types
|
4
|
+
class StringType < BaseType
|
5
|
+
class StringSampler
|
6
|
+
delegate *Faker::Address.methods(false), to: Faker::Address
|
7
|
+
delegate *Faker::Code.methods(false), to: Faker::Code
|
8
|
+
delegate :suffix, :catch_phrase, :bs, :ein, :duns_number, :logo, to: Faker::Company
|
9
|
+
alias_method :slogan, :catch_phrase
|
10
|
+
delegate :name, to: Faker::Company, prefix: :company
|
11
|
+
alias_method :company, :company_name
|
12
|
+
delegate :credit_card, to: Faker::Finance, prefix: :company
|
13
|
+
delegate *Faker::Internet.methods(false), to: Faker::Internet
|
14
|
+
delegate :first_name, :last_name, :prefix, :suffix, :title, to: Faker::Name
|
15
|
+
delegate *Faker::PhoneNumber.methods(false), to: Faker::PhoneNumber
|
16
|
+
alias_method :phone, :phone_number
|
17
|
+
alias_method :mobile, :cell_phone
|
18
|
+
alias_method :cell, :cell_phone
|
19
|
+
delegate *Faker::Commerce.methods(false), to: Faker::Commerce
|
20
|
+
delegate *Faker::Avatar.methods(false), to: Faker::Avatar, prefix: :avatar
|
21
|
+
alias_method :avatar_url, :avatar_image
|
22
|
+
alias_method :profile_image, :avatar_image
|
23
|
+
alias_method :profile_pic, :avatar_image
|
24
|
+
delegate :birthday, to: Faker::Date
|
25
|
+
|
26
|
+
def initialize(field_name)
|
27
|
+
@field_name = field_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def hacker_speak
|
31
|
+
Faker::Hacker.say_something_smart
|
32
|
+
end
|
33
|
+
|
34
|
+
def full_name
|
35
|
+
Faker::Name.name
|
36
|
+
end
|
37
|
+
|
38
|
+
def domain
|
39
|
+
[Faker::Internet.domain_word, Faker::Internet.domain_suffix].join '.'
|
40
|
+
end
|
41
|
+
|
42
|
+
def description
|
43
|
+
Faker::Lorem.paragraph
|
44
|
+
end
|
45
|
+
|
46
|
+
alias_method :body, :description
|
47
|
+
alias_method :content, :description
|
48
|
+
alias_method :prompt, :description
|
49
|
+
|
50
|
+
def value
|
51
|
+
if self.class.instance_methods(false).include?(@field_name)
|
52
|
+
public_send(@field_name)
|
53
|
+
elsif (field = self.class.instance_methods(false).find { |m| @field_name.to_s.include? m.to_s })
|
54
|
+
public_send(field)
|
55
|
+
else
|
56
|
+
Faker::Lorem.word
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def sample(field_name)
|
63
|
+
StringSampler.new(field_name).value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'faker'
|
2
|
+
|
3
|
+
module JSONAPIonify::Types
|
4
|
+
class TimeStringType < BaseType
|
5
|
+
def load(value)
|
6
|
+
Time.parse value
|
7
|
+
end
|
8
|
+
|
9
|
+
def dump(value)
|
10
|
+
case value
|
11
|
+
when Time
|
12
|
+
JSON.dump(value.to_time)
|
13
|
+
else
|
14
|
+
raise TypeError, "#{value} is not a valid JSON #{name}."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def sample(field_name)
|
19
|
+
field_name = field_name.to_s
|
20
|
+
if field_name.to_s.end_with?('ed_at') || field_name.include?('start')
|
21
|
+
Faker::Time.backward
|
22
|
+
elsif field_name.include?('end')
|
23
|
+
Faker::Time.forward
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'oj'
|
2
|
+
|
3
|
+
module JSONAPIonify::Types
|
4
|
+
extend JSONAPIonify::Autoload
|
5
|
+
autoload_all
|
6
|
+
|
7
|
+
def types
|
8
|
+
DefinitionFinder
|
9
|
+
end
|
10
|
+
|
11
|
+
module DefinitionFinder
|
12
|
+
def self.method_missing(m, *args)
|
13
|
+
JSONAPIonify::Types.const_get("#{m}Type", false).new(*args)
|
14
|
+
rescue NameError
|
15
|
+
raise TypeError, "#{m} is not a valid JSON type."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class BaseType
|
20
|
+
include JSONAPIonify::Callbacks
|
21
|
+
define_callbacks :initialize
|
22
|
+
|
23
|
+
def name
|
24
|
+
self.class.name.split('::').last.chomp('Type')
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :options
|
28
|
+
|
29
|
+
def initialize(**options)
|
30
|
+
run_callbacks :initialize do
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
freeze
|
34
|
+
end
|
35
|
+
|
36
|
+
def load(non_ruby)
|
37
|
+
non_ruby
|
38
|
+
end
|
39
|
+
|
40
|
+
def dump(ruby)
|
41
|
+
JSON.load JSON.dump ruby
|
42
|
+
end
|
43
|
+
|
44
|
+
def verify(non_ruby)
|
45
|
+
dump(load(non_ruby)) == non_ruby
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module JSONAPIonify
|
2
|
+
module UnstrictProc
|
3
|
+
refine Proc do
|
4
|
+
def unstrict
|
5
|
+
return self unless lambda?
|
6
|
+
req_count = required_parameters.count
|
7
|
+
Proc.new do |*arguments|
|
8
|
+
if req_count > arguments.count
|
9
|
+
(req_count - arguments.count).times { arguments << nil }
|
10
|
+
elsif req_count < arguments.count
|
11
|
+
arguments = req_count == 0 ? [] : arguments[0..(req_count - 1)]
|
12
|
+
end
|
13
|
+
call(*arguments)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def parameters_hash
|
20
|
+
parameters.each_with_object({}) { |(k, v), h| (h[k] ||= []) << v }
|
21
|
+
end
|
22
|
+
|
23
|
+
def required_parameters
|
24
|
+
parameters_hash[:req] || []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/jsonapionify.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'pry' rescue nil
|
2
|
+
require 'core_ext/boolean'
|
3
|
+
require "active_support/core_ext/string/inflections"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
5
|
+
require 'active_support/cache'
|
6
|
+
require 'jsonapionify/autoload'
|
7
|
+
|
8
|
+
module JSONAPIonify
|
9
|
+
autoload :VERSION, 'jsonapi-objects/version'
|
10
|
+
extend JSONAPIonify::Autoload
|
11
|
+
autoload_all 'jsonapionify'
|
12
|
+
|
13
|
+
def self.path
|
14
|
+
__dir__
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.parse(hash)
|
18
|
+
hash = JSON.parse(hash) if hash.is_a? String
|
19
|
+
Structure::Objects::TopLevel.from_hash(hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.new_object
|
23
|
+
Structure::Objects::TopLevel.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.cache(store, *args)
|
27
|
+
self.cache_store = ActiveSupport::Cache.lookup_store(store, *args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.cache_store=(store)
|
31
|
+
@cache_store = store
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.cache_store
|
35
|
+
@cache_store ||= ActiveSupport::Cache.lookup_store :null_store
|
36
|
+
end
|
37
|
+
end
|