whisperer 0.0.1

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.
Files changed (88) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +293 -0
  8. data/Rakefile +1 -0
  9. data/TODO.md +47 -0
  10. data/lib/whisperer.rb +102 -0
  11. data/lib/whisperer/config.rb +40 -0
  12. data/lib/whisperer/convertors/hash.rb +39 -0
  13. data/lib/whisperer/convertors/interaction.rb +21 -0
  14. data/lib/whisperer/dsl.rb +19 -0
  15. data/lib/whisperer/dsl/base.rb +47 -0
  16. data/lib/whisperer/dsl/body.rb +33 -0
  17. data/lib/whisperer/dsl/headers.rb +20 -0
  18. data/lib/whisperer/dsl/request.rb +15 -0
  19. data/lib/whisperer/dsl/response.rb +15 -0
  20. data/lib/whisperer/dsl/response/status.rb +10 -0
  21. data/lib/whisperer/generator.rb +43 -0
  22. data/lib/whisperer/helpers.rb +16 -0
  23. data/lib/whisperer/placeholder.rb +14 -0
  24. data/lib/whisperer/preprocessors.rb +19 -0
  25. data/lib/whisperer/preprocessors/base.rb +13 -0
  26. data/lib/whisperer/preprocessors/content_length.rb +17 -0
  27. data/lib/whisperer/preprocessors/response_body.rb +23 -0
  28. data/lib/whisperer/record.rb +48 -0
  29. data/lib/whisperer/record/body.rb +15 -0
  30. data/lib/whisperer/record/headers.rb +22 -0
  31. data/lib/whisperer/record/request.rb +16 -0
  32. data/lib/whisperer/record/response.rb +18 -0
  33. data/lib/whisperer/record/response/status.rb +10 -0
  34. data/lib/whisperer/samples/cassette_builder.rb +24 -0
  35. data/lib/whisperer/serializers/base.rb +24 -0
  36. data/lib/whisperer/serializers/json.rb +33 -0
  37. data/lib/whisperer/serializers/json_multiple.rb +14 -0
  38. data/lib/whisperer/tasks/whisperer.rake +88 -0
  39. data/lib/whisperer/version.rb +3 -0
  40. data/spec/cassette_builders/arya_stark.rb +26 -0
  41. data/spec/cassette_builders/bran_stark.rb +19 -0
  42. data/spec/cassette_builders/empty_robb_stark.rb +20 -0
  43. data/spec/cassette_builders/robb_stark.rb +30 -0
  44. data/spec/cassette_builders/robb_stark_without_content_length.rb +20 -0
  45. data/spec/cassette_builders/sansa_stark.rb +9 -0
  46. data/spec/cassette_builders/starks.rb +31 -0
  47. data/spec/cassette_builders/wolfs.rb +7 -0
  48. data/spec/cassettes/empty_robb_stark.yml +22 -0
  49. data/spec/cassettes/girls/arya_stark.yml +24 -0
  50. data/spec/cassettes/robb_stark.yml +28 -0
  51. data/spec/cassettes/robb_stark_without_content_length.yml +22 -0
  52. data/spec/cassettes/sansa_stark.yml +24 -0
  53. data/spec/cassettes/starks.yml +28 -0
  54. data/spec/cassettes/wolfs.yml +28 -0
  55. data/spec/factories/arya_stark.rb +7 -0
  56. data/spec/factories/bran_stark.rb +7 -0
  57. data/spec/factories/ned_stark.rb +7 -0
  58. data/spec/factories/robb_stark.rb +7 -0
  59. data/spec/factories/sansa_stark.rb +7 -0
  60. data/spec/integration/whisperer_spec.rb +51 -0
  61. data/spec/spec_helper.rb +25 -0
  62. data/spec/spec_integration_helper.rb +6 -0
  63. data/spec/support/cassettes.rb +16 -0
  64. data/spec/support/custom_serializer.rb +5 -0
  65. data/spec/unit/config_spec.rb +94 -0
  66. data/spec/unit/convertors/hash_spec.rb +59 -0
  67. data/spec/unit/convertors/interaction_spec.rb +46 -0
  68. data/spec/unit/dsl/base_spec.rb +99 -0
  69. data/spec/unit/dsl/body_spec.rb +73 -0
  70. data/spec/unit/dsl/headers_spec.rb +31 -0
  71. data/spec/unit/dsl/request_spec.rb +4 -0
  72. data/spec/unit/dsl/response_spec.rb +4 -0
  73. data/spec/unit/dsl/status_spec.rb +4 -0
  74. data/spec/unit/dsl_spec.rb +4 -0
  75. data/spec/unit/generator_spec.rb +77 -0
  76. data/spec/unit/helpers_spec.rb +38 -0
  77. data/spec/unit/preprocessors/content_length_spec.rb +39 -0
  78. data/spec/unit/preprocessors/response_body_spec.rb +55 -0
  79. data/spec/unit/preprocessors_spec.rb +8 -0
  80. data/spec/unit/record/headers_spec.rb +21 -0
  81. data/spec/unit/record/response_spec.rb +11 -0
  82. data/spec/unit/record_spec.rb +77 -0
  83. data/spec/unit/serializers/base_spec.rb +19 -0
  84. data/spec/unit/serializers/json_multiple_spec.rb +24 -0
  85. data/spec/unit/serializers/json_spec.rb +37 -0
  86. data/spec/unit/whisperer_spec.rb +189 -0
  87. data/whisperer.gemspec +28 -0
  88. metadata +277 -0
@@ -0,0 +1,15 @@
1
+ module Whisperer
2
+ # This class is used for request and response parts of HTTP
3
+ # interactions, it should include only common attributes and methods.
4
+ class Body
5
+ include Virtus.model
6
+
7
+ attribute :encoding, String
8
+ attribute :string, String
9
+
10
+ # Attributes which are not part of Vcr response
11
+ attribute :data_obj, Object
12
+ attribute :serializer, Symbol, default: proc { :json }
13
+ attribute :serializer_opts, Hash, default: {}
14
+ end # class Body
15
+ end # module Whisperer
@@ -0,0 +1,22 @@
1
+ module Whisperer
2
+ class Headers
3
+ include Virtus.model
4
+
5
+ def initialize(*args)
6
+ extend Virtus.model
7
+
8
+ super
9
+ end
10
+
11
+ def to_hash
12
+ prepared_attrs, attrs = {}, super
13
+
14
+ attrs.each do |key, val|
15
+ key = key.to_s.titleize.split(' ').join('-')
16
+ prepared_attrs[key] = val
17
+ end
18
+
19
+ prepared_attrs
20
+ end
21
+ end # class Header
22
+ end # module Whisperer
@@ -0,0 +1,16 @@
1
+ require_relative 'headers'
2
+ require_relative 'body'
3
+
4
+ module Whisperer
5
+ class Request
6
+ include Virtus.model
7
+
8
+ attribute :uri, String
9
+ attribute :method, Symbol
10
+ attribute :headers, Whisperer::Headers, default: proc {
11
+ Whisperer::Headers.new
12
+ }
13
+
14
+ attribute :body, Body, default: proc { Body.new }
15
+ end # class Request
16
+ end # module Whisperer
@@ -0,0 +1,18 @@
1
+ require_relative 'headers'
2
+ require_relative 'body'
3
+ require_relative 'response/status'
4
+
5
+ module Whisperer
6
+ class Response
7
+ include Virtus.model
8
+
9
+ attribute :headers, Whisperer::Headers, default: proc {
10
+ header = Whisperer::Headers.new
11
+ header.attribute(:content_length, String)
12
+ header
13
+ }
14
+
15
+ attribute :body, Body, default: proc { Body.new }
16
+ attribute :status, Status, default: proc { Status.new }
17
+ end # class Response
18
+ end # module Whisperer
@@ -0,0 +1,10 @@
1
+ module Whisperer
2
+ class Response
3
+ class Status
4
+ include Virtus.model
5
+
6
+ attribute :code, Integer, default: 200
7
+ attribute :message, String, default: 'OK'
8
+ end # class Status
9
+ end # class Response
10
+ end # module Whisperer
@@ -0,0 +1,24 @@
1
+ Whisperer.define(:my_fixture_builder) do
2
+ request do
3
+ uri 'http://example.com/users/1'
4
+ method :get
5
+ end
6
+
7
+ response do
8
+ status do
9
+ code 200
10
+ message 'OK'
11
+ end
12
+
13
+ headers do
14
+ content_type 'application/json;charset=utf-8'
15
+ end
16
+
17
+ body do
18
+ encoding 'UTF-8'
19
+ factory 'my_factory', :json
20
+ end
21
+ end
22
+
23
+ recorded_at Time.new
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'multi_json'
2
+
3
+ require_relative 'base'
4
+
5
+ module Whisperer
6
+ module Serializers
7
+ class Base
8
+ extend Helpers
9
+
10
+ add_builder :serialize
11
+
12
+ attr_reader :options
13
+
14
+ def initialize(obj, options: {})
15
+ @obj = obj
16
+ @options = options
17
+ end
18
+
19
+ def serialize
20
+ raise NotImplementedError
21
+ end
22
+ end # class Base
23
+ end # module Serializers
24
+ end # module Whisperer
@@ -0,0 +1,33 @@
1
+ require 'multi_json'
2
+ require_relative 'base'
3
+
4
+ module Whisperer
5
+ module Serializers
6
+ class Json < Base
7
+ def initialize(obj, options: {}, json_dumper: MultiJson)
8
+ super obj, options: options
9
+
10
+ @json_dumper = json_dumper
11
+ end
12
+
13
+ def serialize
14
+ data = prepare_data
15
+ data = post_prepare_data(data)
16
+
17
+ @json_dumper.dump(data)
18
+ end
19
+
20
+ protected
21
+ def prepare_data
22
+ @obj.marshal_dump
23
+ end
24
+
25
+ # This method returns give data as it is.
26
+ # The purpose of this method is to give a way in child classes
27
+ # to alter data structure before converting data.
28
+ def post_prepare_data(data)
29
+ data
30
+ end
31
+ end # class Json
32
+ end # module Serializers
33
+ end # module Whisperer
@@ -0,0 +1,14 @@
1
+ require_relative 'json'
2
+
3
+ module Whisperer
4
+ module Serializers
5
+ class JsonMultiple < Json
6
+ protected
7
+ def prepare_data
8
+ @obj.map do |item|
9
+ item.marshal_dump
10
+ end
11
+ end
12
+ end # class JsonMultiple
13
+ end # module Serializers
14
+ end # module Whisperer
@@ -0,0 +1,88 @@
1
+ require 'whisperer'
2
+
3
+ require 'active_support/core_ext/hash/deep_merge'
4
+ require 'factory_girl'
5
+ require 'rainbow'
6
+ require 'yaml'
7
+
8
+ config = Whisperer::Config.load('.whisperer.yml')
9
+
10
+ Dir[
11
+ config.factories_matcher,
12
+ config.builders_matcher
13
+ ].each {|f| require f }
14
+
15
+ namespace :whisperer do
16
+ desc 'Creates minimal structure of directories, creates a config file with default options'
17
+ task install: ['config:create'] do
18
+ path_to_builders = 'spec/cassette_builders'
19
+
20
+ if Dir.exists?(path_to_builders)
21
+ puts Rainbow("Skipped creating of #{path_to_builders} since it already exists").green
22
+ else
23
+ Dir.mkdir(path_to_builders)
24
+
25
+ puts Rainbow("Created directory for cassette builders: #{path_to_builders}").green
26
+ end
27
+ end
28
+
29
+ namespace :config do
30
+ desc 'Creates a config file with default options'
31
+ task :create do
32
+ if File.exists?('.whisperer.yml')
33
+ puts Rainbow("Skipped creating the sample of config (.whisperer.yml) since it already exists").green
34
+ else
35
+ File.open(
36
+ '.whisperer.yml',
37
+ File::CREAT|File::RDWR,
38
+ 0644
39
+ ) do |f|
40
+ f.write(config.to_yml)
41
+
42
+ f.close
43
+ end
44
+
45
+ puts Rainbow("Created the sample of config: .whisperer.yml").green
46
+ end
47
+ end
48
+ end
49
+
50
+ namespace :cassettes do
51
+ desc 'Takes all cassette builders and generates cassettes for VCR'
52
+ task :generate_all do
53
+ begin
54
+ Whisperer.generate_all
55
+
56
+ puts Rainbow('cassettes are generated').green
57
+ rescue Whisperer::NocassetteRecordError => error
58
+ puts Rainbow("Any cassette builder was found. Please, make sure you define at least one (We are looking for it like: #{config.builders_matcher}).").yellow
59
+ end
60
+ end
61
+
62
+ desc 'Takes a specific cassette builder and generates one specific cassette for VCR'
63
+ task :generate, :name do |t, args|
64
+ name = args[:name]
65
+
66
+ begin
67
+ Whisperer::generate(name)
68
+
69
+ puts Rainbow("The cassette '#{name}' is generated").green
70
+ rescue Whisperer::NocassetteRecordError => error
71
+ puts Rainbow(error.message).red
72
+ end
73
+ end
74
+
75
+ namespace :builders do
76
+ desc 'Creates a sample of the cassette builder'
77
+ task :sample do
78
+ sample = File.join(File.dirname(__FILE__), '../samples/cassette_builder.rb')
79
+
80
+ path_to_save = config.path_to_builders + '/sample.rb'
81
+
82
+ FileUtils.cp(sample, path_to_save)
83
+
84
+ puts Rainbow("Created the sample of the cassette builder: #{path_to_save}").green
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,3 @@
1
+ module Whisperer
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,26 @@
1
+ Whisperer.define(:arya_stark) do
2
+ request do
3
+ uri 'http://example.com/users/1'
4
+ method :get
5
+ end
6
+
7
+ response do
8
+ status do
9
+ code 200
10
+ message 'OK'
11
+ end
12
+
13
+ headers do
14
+ content_type 'application/json;charset=utf-8'
15
+ end
16
+
17
+ body do
18
+ encoding 'UTF-8'
19
+ factory 'arya_stark'
20
+ end
21
+ end
22
+
23
+ recorded_at 'Mon, 13 Jan 2014 21:01:47 GMT'
24
+
25
+ sub_path 'girls'
26
+ end
@@ -0,0 +1,19 @@
1
+ Whisperer.define(:bran_stark) do
2
+ request do
3
+ uri 'http://example.com/users/2'
4
+ method :get
5
+
6
+ headers do
7
+ accept '*/*'
8
+ end
9
+ end
10
+
11
+ response do
12
+ body do
13
+ serializer :custom
14
+ factory 'bran_stark'
15
+ end
16
+ end
17
+
18
+ recorded_at 'Mon, 13 Jan 2014 21:01:47 GMT'
19
+ end
@@ -0,0 +1,20 @@
1
+ Whisperer.define(:empty_robb_stark) do
2
+ request do
3
+ uri 'http://example.com/users/2'
4
+ method :get
5
+ end
6
+
7
+ response do
8
+ status do
9
+ code 200
10
+ message 'OK'
11
+ end
12
+
13
+ body do
14
+ encoding 'UTF-8'
15
+ string '{"empty":true}'
16
+ end
17
+ end
18
+
19
+ recorded_at 'Mon, 13 Jan 2014 21:01:47 GMT'
20
+ end
@@ -0,0 +1,30 @@
1
+ Whisperer.define(:robb_stark) do
2
+ request do
3
+ uri 'http://example.com/users/1'
4
+ method :get
5
+
6
+ headers do
7
+ accept '*/*'
8
+ end
9
+ end
10
+
11
+ response do
12
+ status do
13
+ code 200
14
+ message 'OK'
15
+ end
16
+
17
+ headers do
18
+ content_type 'application/json;charset=utf-8'
19
+ content_length 57
20
+ x_content_type_options 'nosniff'
21
+ end
22
+
23
+ body do
24
+ encoding 'UTF-8'
25
+ factory 'robb_stark'
26
+ end
27
+ end
28
+
29
+ recorded_at 'Mon, 13 Jan 2014 21:01:47 GMT'
30
+ end
@@ -0,0 +1,20 @@
1
+ Whisperer.define(:robb_stark_without_content_length) do
2
+ request do
3
+ uri 'http://example.com/users/2'
4
+ method :get
5
+ end
6
+
7
+ response do
8
+ status do
9
+ code 200
10
+ message 'OK'
11
+ end
12
+
13
+ body do
14
+ encoding 'UTF-8'
15
+ factory 'robb_stark'
16
+ end
17
+ end
18
+
19
+ recorded_at 'Mon, 13 Jan 2014 21:01:47 GMT'
20
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'bran_stark'
2
+
3
+ Whisperer.define(:sansa_stark, parent: :bran_stark) do
4
+ response do
5
+ body do
6
+ factory 'sansa_stark'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ Whisperer.define(:starks) do
2
+ request do
3
+ uri 'http://example.com/users'
4
+ method :get
5
+
6
+ headers do
7
+ accept '*/*'
8
+ end
9
+ end
10
+
11
+ response do
12
+ status do
13
+ code 200
14
+ message 'OK'
15
+ end
16
+
17
+ headers do
18
+ content_type 'application/json;charset=utf-8'
19
+ content_length 57
20
+ x_content_type_options 'nosniff'
21
+ end
22
+
23
+ body do
24
+ encoding 'UTF-8'
25
+ serializer :json_multiple
26
+ factories ['robb_stark', 'ned_stark']
27
+ end
28
+ end
29
+
30
+ recorded_at 'Mon, 13 Jan 2014 21:01:47 GMT'
31
+ end