representors 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +18 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +126 -0
  5. data/LICENSE.md +19 -0
  6. data/README.md +28 -0
  7. data/Rakefile +10 -0
  8. data/lib/representor_support/utilities.rb +39 -0
  9. data/lib/representors.rb +5 -0
  10. data/lib/representors/errors.rb +7 -0
  11. data/lib/representors/field.rb +108 -0
  12. data/lib/representors/options.rb +67 -0
  13. data/lib/representors/representor.rb +161 -0
  14. data/lib/representors/representor_builder.rb +64 -0
  15. data/lib/representors/representor_hash.rb +59 -0
  16. data/lib/representors/serialization.rb +4 -0
  17. data/lib/representors/serialization/deserializer_base.rb +29 -0
  18. data/lib/representors/serialization/deserializer_factory.rb +13 -0
  19. data/lib/representors/serialization/hal_deserializer.rb +44 -0
  20. data/lib/representors/serialization/hal_serializer.rb +91 -0
  21. data/lib/representors/serialization/hale_deserializer.rb +162 -0
  22. data/lib/representors/serialization/hale_serializer.rb +110 -0
  23. data/lib/representors/serialization/serialization_base.rb +27 -0
  24. data/lib/representors/serialization/serialization_factory_base.rb +54 -0
  25. data/lib/representors/serialization/serializer_base.rb +20 -0
  26. data/lib/representors/serialization/serializer_factory.rb +17 -0
  27. data/lib/representors/transition.rb +130 -0
  28. data/lib/representors/version.rb +4 -0
  29. data/spec/fixtures/complex_hal.json +92 -0
  30. data/spec/fixtures/complex_hale_document.json +81 -0
  31. data/spec/fixtures/drds_hash.rb +120 -0
  32. data/spec/fixtures/hale_spec_examples/basic.json +77 -0
  33. data/spec/fixtures/hale_spec_examples/complex_reference_objects.json +157 -0
  34. data/spec/fixtures/hale_spec_examples/data.json +17 -0
  35. data/spec/fixtures/hale_spec_examples/data_objects.json +96 -0
  36. data/spec/fixtures/hale_spec_examples/link_objects.json +18 -0
  37. data/spec/fixtures/hale_spec_examples/nested_ref.json +43 -0
  38. data/spec/fixtures/hale_spec_examples/reference_objects.json +89 -0
  39. data/spec/fixtures/hale_tutorial_examples/basic_links.json +85 -0
  40. data/spec/fixtures/hale_tutorial_examples/basic_links_with_orders.json +96 -0
  41. data/spec/fixtures/hale_tutorial_examples/basic_links_with_references.json +108 -0
  42. data/spec/fixtures/hale_tutorial_examples/embedded.json +182 -0
  43. data/spec/fixtures/hale_tutorial_examples/empty.json +1 -0
  44. data/spec/fixtures/hale_tutorial_examples/enctype.json +14 -0
  45. data/spec/fixtures/hale_tutorial_examples/final.json +141 -0
  46. data/spec/fixtures/hale_tutorial_examples/get_link.json +17 -0
  47. data/spec/fixtures/hale_tutorial_examples/get_link_with_data.json +29 -0
  48. data/spec/fixtures/hale_tutorial_examples/links.json +11 -0
  49. data/spec/fixtures/hale_tutorial_examples/links_only.json +3 -0
  50. data/spec/fixtures/hale_tutorial_examples/meta.json +208 -0
  51. data/spec/fixtures/hale_tutorial_examples/self_link.json +7 -0
  52. data/spec/fixtures/single_drd.rb +266 -0
  53. data/spec/lib/representors/complex_representor_spec.rb +288 -0
  54. data/spec/lib/representors/field_spec.rb +141 -0
  55. data/spec/lib/representors/representor_builder_spec.rb +223 -0
  56. data/spec/lib/representors/representor_spec.rb +285 -0
  57. data/spec/lib/representors/serialization/deserializer_factory_spec.rb +118 -0
  58. data/spec/lib/representors/serialization/hal_deserializer_spec.rb +34 -0
  59. data/spec/lib/representors/serialization/hal_serializer_spec.rb +171 -0
  60. data/spec/lib/representors/serialization/hale_deserializer_spec.rb +59 -0
  61. data/spec/lib/representors/serialization/hale_roundtrip_spec.rb +34 -0
  62. data/spec/lib/representors/serialization/hale_serializer_spec.rb +659 -0
  63. data/spec/lib/representors/serialization/serializer_factory_spec.rb +108 -0
  64. data/spec/lib/representors/transition_spec.rb +349 -0
  65. data/spec/spec_helper.rb +32 -0
  66. data/spec/support/basic-hale.json +12 -0
  67. data/spec/support/hal_representor_shared.rb +206 -0
  68. data/spec/support/helpers.rb +8 -0
  69. data/tasks/benchmark.rake +75 -0
  70. data/tasks/complex_hal_document.json +98 -0
  71. data/tasks/test_specs.rake +37 -0
  72. data/tasks/yard.rake +22 -0
  73. metadata +232 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 302a3db2817d8fe764b0ee2649ce4771f1d57a40
4
+ data.tar.gz: 38cb226342fa028833cc3582a85832838e86820f
5
+ SHA512:
6
+ metadata.gz: 65ed976754d9aac6f894b55b13e08aba69c18ec08a394efd000233552b4f95d6691c098c712592981ea2b7bca0b4a0e7a18d5e85646129750252a57919826c0b
7
+ data.tar.gz: 5d63a44bbd9e5419e431f4045e4c7b166343281b1029a5a3277ecc79dfc2340c0edb7d6fdba3a0cafb03e7660283b5589ada30dd40dee08a69b4841347f1632e
@@ -0,0 +1,18 @@
1
+ # 0.0.5
2
+ * Sync changes from the develop branch that had not been ported back to 0-0-stable (see PR #52 and PR #53)
3
+
4
+ # 0.0.4
5
+ * Updates and clean ups
6
+ * Remove support of Ruby < 2.1
7
+
8
+ # 0.0.3
9
+ * Update to latest rspec and mutant
10
+ * Implement URL templating
11
+ * Change on behaviour of `Representor::Transition#templated_uri`. It only provides a template URL if the original URL followed the RFC 6570.
12
+
13
+ # 0.0.2
14
+ * Significant refactor of Builder to be non mutating
15
+ * Includes Hale deserializer
16
+
17
+ # 0.0.1
18
+ * Initial Release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,126 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ representors (0.0.3)
5
+ addressable (~> 2.3)
6
+ rake
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ abstract_type (0.0.7)
12
+ adamantium (0.2.0)
13
+ ice_nine (~> 0.11.0)
14
+ memoizable (~> 0.4.0)
15
+ addressable (2.3.7)
16
+ anima (0.2.0)
17
+ abstract_type (~> 0.0.7)
18
+ adamantium (~> 0.1)
19
+ equalizer (~> 0.0.8)
20
+ ast (2.0.0)
21
+ awesome_print (1.2.0)
22
+ byebug (3.2.0)
23
+ columnize (~> 0.8)
24
+ debugger-linecache (~> 1.2)
25
+ coderay (1.1.0)
26
+ columnize (0.9.0)
27
+ concord (0.1.5)
28
+ adamantium (~> 0.2.0)
29
+ equalizer (~> 0.0.9)
30
+ debugger-linecache (1.2.0)
31
+ diff-lcs (1.2.5)
32
+ docile (1.1.5)
33
+ equalizer (0.0.10)
34
+ ice_nine (0.11.1)
35
+ memoizable (0.4.2)
36
+ thread_safe (~> 0.3, >= 0.3.1)
37
+ method_source (0.8.2)
38
+ morpher (0.2.3)
39
+ abstract_type (~> 0.0.7)
40
+ adamantium (~> 0.2.0)
41
+ anima (~> 0.2.0)
42
+ ast (~> 2.0.0)
43
+ concord (~> 0.1.4)
44
+ equalizer (~> 0.0.9)
45
+ ice_nine (~> 0.11.0)
46
+ procto (~> 0.0.2)
47
+ multi_json (1.10.1)
48
+ mutant (0.7.8)
49
+ abstract_type (~> 0.0.7)
50
+ adamantium (~> 0.2.0)
51
+ anima (~> 0.2.0)
52
+ ast (~> 2.0)
53
+ concord (~> 0.1.5)
54
+ diff-lcs (~> 1.2)
55
+ equalizer (~> 0.0.9)
56
+ ice_nine (~> 0.11.1)
57
+ memoizable (~> 0.4.2)
58
+ morpher (~> 0.2.3)
59
+ parallel (~> 1.3)
60
+ parser (~> 2.2.0.2)
61
+ procto (~> 0.0.2)
62
+ unparser (~> 0.2.2)
63
+ mutant-rspec (0.7.8)
64
+ mutant (~> 0.7.8)
65
+ rspec-core (>= 3.0.0, < 3.3.0)
66
+ parallel (1.4.1)
67
+ parser (2.2.0.3)
68
+ ast (>= 1.1, < 3.0)
69
+ procto (0.0.2)
70
+ pry (0.9.12.6)
71
+ coderay (~> 1.0)
72
+ method_source (~> 0.8)
73
+ slop (~> 3.4)
74
+ pry-nav (0.2.3)
75
+ pry (~> 0.9.10)
76
+ pry-remote (0.1.8)
77
+ pry (~> 0.9)
78
+ slop (~> 3.0)
79
+ rake (10.4.2)
80
+ redcarpet (3.1.2)
81
+ rspec (3.2.0)
82
+ rspec-core (~> 3.2.0)
83
+ rspec-expectations (~> 3.2.0)
84
+ rspec-mocks (~> 3.2.0)
85
+ rspec-core (3.2.2)
86
+ rspec-support (~> 3.2.0)
87
+ rspec-expectations (3.2.0)
88
+ diff-lcs (>= 1.2.0, < 2.0)
89
+ rspec-support (~> 3.2.0)
90
+ rspec-mocks (3.2.1)
91
+ diff-lcs (>= 1.2.0, < 2.0)
92
+ rspec-support (~> 3.2.0)
93
+ rspec-support (3.2.2)
94
+ simplecov (0.8.2)
95
+ docile (~> 1.1.0)
96
+ multi_json
97
+ simplecov-html (~> 0.8.0)
98
+ simplecov-html (0.8.0)
99
+ slop (3.5.0)
100
+ thread_safe (0.3.5)
101
+ unparser (0.2.2)
102
+ abstract_type (~> 0.0.7)
103
+ adamantium (~> 0.2.0)
104
+ concord (~> 0.1.5)
105
+ diff-lcs (~> 1.2.5)
106
+ equalizer (~> 0.0.9)
107
+ parser (~> 2.2.0.2)
108
+ procto (~> 0.0.2)
109
+ yard (0.8.7.4)
110
+
111
+ PLATFORMS
112
+ ruby
113
+
114
+ DEPENDENCIES
115
+ awesome_print (~> 1.2.0)
116
+ byebug (~> 3.2)
117
+ mutant (~> 0.7.8)
118
+ mutant-rspec (~> 0.7.8)
119
+ pry
120
+ pry-nav
121
+ pry-remote
122
+ redcarpet (~> 3.1.1)
123
+ representors!
124
+ rspec (~> 3.2)
125
+ simplecov (~> 0.8.2)
126
+ yard (~> 0.8.7)
@@ -0,0 +1,19 @@
1
+ Copyright &copy; 2014 Medidata Solutions Worldwide
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ # Representors
2
+
3
+ A library to simplify Hypermedia message representation. It has the knowledge of Hypermedia media-types from the Ancients!
4
+
5
+ This gem provides serializers and deserializers from/to known Hypermedia formats. It currently supports HAL and HALE.
6
+ It also provides a Representor class to hold the information from hypermedia responses, this class provides method to access properties, transitions, etc.
7
+
8
+
9
+ ## Developing
10
+
11
+ Write your tests, write your code and make sure all tests pass:
12
+ ```
13
+ bundle exec rspec
14
+ ```
15
+
16
+ Also, you can check your test coverage by running mutant on the classes you have worked on.
17
+ For instance if you modified Representors::Representor, please execute:
18
+ ```
19
+ MUTANT=true mutant --include lib --require representors --score 95 --use rspec Representors::Representor*
20
+ ```
21
+
22
+ Reaching 100% mutant coverage is not feasible sometimes as they may be some false positives.
23
+ But please investigate any missing coverage, as it may indicate an actual problem with the tests.
24
+
25
+
26
+ ## Copyright
27
+
28
+ Copyright &copy; 2016 Medidata Solutions Worldwide. See [LICENSE](LICENSE.md) for details.
@@ -0,0 +1,10 @@
1
+ lib_dir = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib_dir)
3
+ $LOAD_PATH.uniq!
4
+
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+ require 'rake'
8
+ require 'rake/clean'
9
+
10
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,39 @@
1
+ module RepresentorSupport
2
+ module Utilities
3
+
4
+ # Accepts a hash and returns a new hash with symbolized keys
5
+ def symbolize_keys(hash)
6
+ Hash[hash.map{|(k,v)| [k.to_sym,v]}]
7
+ end
8
+
9
+ # Will recursively deep dup an aribitrary set of nested hashes and arrays,
10
+ # but does not handle complex objects. Also, this benchmarked *way* faster
11
+ # than the Marshal approach.
12
+ def deep_dup(obj)
13
+ if obj.is_a?(Hash)
14
+ result = {}
15
+ obj.each { |k,v| result[k] = deep_dup(v) }
16
+ result
17
+ elsif obj.is_a?(Array)
18
+ obj.map { |el| deep_dup(el) }
19
+ else
20
+ dup_or_self(obj)
21
+ end
22
+ end
23
+
24
+ # Unfortunately the best way to test if you can actually dup an object is to
25
+ # try, and handle the TypeError if not succesful.
26
+ def dup_or_self(obj)
27
+ begin
28
+ obj.dup
29
+ rescue TypeError
30
+ obj
31
+ end
32
+ end
33
+
34
+ def map_or_apply(proc, obj)
35
+ obj.is_a?(Array) ? obj.map { |sub| proc.(sub) } : proc.(obj)
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ require 'representors/representor_hash'
2
+ require 'representors/representor'
3
+ require 'representors/representor_builder'
4
+
5
+ require 'representors/serialization'
@@ -0,0 +1,7 @@
1
+ module Representors
2
+ # General error when there is a problem with any deserialization.
3
+ class DeserializationError < StandardError; end
4
+
5
+ # Error to raise when the media-type we are trying to de/serialize is not known by this gem
6
+ class UnknownMediaTypeError < StandardError; end
7
+ end
@@ -0,0 +1,108 @@
1
+ require 'yaml'
2
+ require 'representors/options'
3
+ require 'representor_support/utilities'
4
+ module Representors
5
+ ##
6
+ # Manages the respresentation of hypermedia fields for different media-types.
7
+ class Field < String
8
+ include RepresentorSupport::Utilities
9
+
10
+ SIMPLE_METHODS = %w(name value default description field_type type data_type cardinality doc).map(&:to_sym)
11
+ DESCRIPTORS_KEY = :descriptors
12
+ ATTRIBUTE_FIELDS = 'attribute'
13
+ PARAMETER_FIELDS = 'href'
14
+ VALIDATORS_KEY = :validators
15
+ OPTIONS_KEY = :options
16
+ SCOPE_KEY = :scope
17
+ NAME_KEY = :name
18
+ DEFAULT_SCOPE = 'attribute'
19
+
20
+ # @example
21
+ # hash = {field_name: {field_property: property_name}}
22
+ # Fields.new(hash)
23
+ # It must only have one key/vale pair where the value is a hash
24
+ # @param [Hash] the abstract representation of a Field
25
+ def initialize(field_hash)
26
+ name = field_hash.keys.first
27
+ field_hash[name] = symbolize_keys(field_hash[name])
28
+ @field_hash = field_hash[name].clone
29
+ @field_hash[NAME_KEY] = name
30
+ super value.to_s
31
+ end
32
+
33
+ SIMPLE_METHODS.each do |meth|
34
+ define_method(meth) { @field_hash[meth] }
35
+ end
36
+
37
+ # @return [String] representing the sort of field
38
+ def scope
39
+ @scope ||= @field_hash[SCOPE_KEY] || DEFAULT_SCOPE
40
+ end
41
+
42
+ # @return [Hash] The hash representation of the object
43
+ def to_hash
44
+ @to_hash ||= { @field_hash[NAME_KEY] => @field_hash.reject {|k,v| k == NAME_KEY } }
45
+ end
46
+
47
+ # @return [String] the yaml representation of the object
48
+ def to_yaml
49
+ @to_yaml ||= YAML.dump(to_hash)
50
+ end
51
+
52
+ # @return [Array] who's elements are all [Hash] objects
53
+ def validators
54
+ @validators ||= if @field_hash.has_key?(VALIDATORS_KEY)
55
+ @field_hash[VALIDATORS_KEY].map { |h| h.instance_of?(Symbol) ? {h => h} : h }
56
+ else
57
+ []
58
+ end
59
+ end
60
+
61
+ # @return [Array] who's elements are all Representors::Options objects
62
+ # TODO Not an array. Its an Options, which should be an array?
63
+ def options
64
+ @options ||= Options.new(@field_hash[OPTIONS_KEY], name)
65
+ end
66
+
67
+ # @returns the value of the field
68
+ def call
69
+ value
70
+ end
71
+
72
+ # The Parameters (i.e. GET variables)
73
+ #
74
+ # @return [Array] who's elements are all <Crichton:Field> objects
75
+ def parameters
76
+ @parameters ||= get_field_by_type(PARAMETER_FIELDS)
77
+ end
78
+
79
+ # The Parameters (i.e. POST variables)
80
+ #
81
+ # @return [Array] who's elements are all <Crichton:Field> objects
82
+ def attributes
83
+ @attributes ||= get_field_by_type(ATTRIBUTE_FIELDS)
84
+ end
85
+ # The Parameters (i.e. GET variables)
86
+ #
87
+ # @return [Array] who's elements are all <Crichton:Field> objects
88
+ def descriptors
89
+ @descriptors ||= (attributes + parameters)
90
+ end
91
+
92
+ private
93
+
94
+ def filtered_fields(fields, scope)
95
+ fields.select { |field| field.scope == scope }
96
+ end
97
+
98
+ def descriptor_fields(hash)
99
+ hash[DESCRIPTORS_KEY].map { |k, v| Field.new({k => v }) }
100
+ end
101
+
102
+ def get_field_by_type(field_type)
103
+ fields = @field_hash.has_key?(DESCRIPTORS_KEY) ? descriptor_fields(@field_hash) : []
104
+ filtered_fields(fields, field_type)
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,67 @@
1
+ module Representors
2
+ ##
3
+ # Manages the respresentation of
4
+ # field options for a hypermedia message.
5
+ class Options
6
+ TYPE_KEYS = %w(external hash list)
7
+ LIST_TYPE = 'list'
8
+ EXTERNAL_TYPE = 'external'
9
+ HASH_TYPE = 'hash'
10
+ DEFAULT_TYPE = LIST_TYPE
11
+ ID_KEY = 'id'
12
+ ID_TEMPLATE = "%s_options"
13
+
14
+ # @param [Hash] the abstract representation of Field Options
15
+ def initialize(options_hash = {}, field_name = '')
16
+ @options_hash = options_hash || {}
17
+ @field_name = field_name
18
+ end
19
+
20
+ # @return [String] delineating the Options type
21
+ def type
22
+ @type ||= begin
23
+ type_keys = TYPE_KEYS.detect { |key| @options_hash.has_key?(key) }
24
+ type_keys || DEFAULT_TYPE
25
+ end
26
+ end
27
+
28
+ # @return [Bool] indicating whether the Options can be treated as a datalist
29
+ def datalist?
30
+ type == EXTERNAL_TYPE || @options_hash.has_key?(ID_KEY)
31
+ end
32
+
33
+ def empty?
34
+ to_hash.empty?
35
+ end
36
+
37
+ # @return [String] representing a unique id for the options
38
+ def id
39
+ @options_hash[ID_KEY] || ID_TEMPLATE % @field_name
40
+ end
41
+
42
+ # @return [Hash] version of the Options
43
+ def to_hash
44
+ type == LIST_TYPE ? Hash[(@options_hash[LIST_TYPE] || {}).map { |x| [x, x] }] : @options_hash[type]
45
+ end
46
+
47
+ # @return [Array] hash keys
48
+ def keys
49
+ to_hash.keys
50
+ end
51
+
52
+ # @return [Array] hash values
53
+ def values
54
+ to_hash.values
55
+ end
56
+
57
+ # @return [Array] version of the Options
58
+ def to_list
59
+ keys
60
+ end
61
+
62
+ def to_data
63
+ type == HASH_TYPE || type == EXTERNAL_TYPE ? to_hash : to_list
64
+ end
65
+
66
+ end
67
+ end