pliny 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/bin/pliny-generate +6 -0
  3. data/lib/pliny.rb +21 -0
  4. data/lib/pliny/commands/generator.rb +197 -0
  5. data/lib/pliny/config_helpers.rb +24 -0
  6. data/lib/pliny/errors.rb +109 -0
  7. data/lib/pliny/extensions/instruments.rb +38 -0
  8. data/lib/pliny/log.rb +84 -0
  9. data/lib/pliny/middleware/cors.rb +46 -0
  10. data/lib/pliny/middleware/request_id.rb +42 -0
  11. data/lib/pliny/middleware/request_store.rb +14 -0
  12. data/lib/pliny/middleware/rescue_errors.rb +24 -0
  13. data/lib/pliny/middleware/timeout.rb +25 -0
  14. data/lib/pliny/middleware/versioning.rb +64 -0
  15. data/lib/pliny/request_store.rb +22 -0
  16. data/lib/pliny/router.rb +15 -0
  17. data/lib/pliny/tasks.rb +3 -0
  18. data/lib/pliny/tasks/db.rake +116 -0
  19. data/lib/pliny/tasks/test.rake +8 -0
  20. data/lib/pliny/templates/endpoint.erb +30 -0
  21. data/lib/pliny/templates/endpoint_acceptance_test.erb +40 -0
  22. data/lib/pliny/templates/endpoint_scaffold.erb +49 -0
  23. data/lib/pliny/templates/endpoint_scaffold_acceptance_test.erb +55 -0
  24. data/lib/pliny/templates/endpoint_test.erb +16 -0
  25. data/lib/pliny/templates/mediator.erb +22 -0
  26. data/lib/pliny/templates/mediator_test.erb +5 -0
  27. data/lib/pliny/templates/migration.erb +9 -0
  28. data/lib/pliny/templates/model.erb +5 -0
  29. data/lib/pliny/templates/model_migration.erb +10 -0
  30. data/lib/pliny/templates/model_test.erb +5 -0
  31. data/lib/pliny/utils.rb +31 -0
  32. data/lib/pliny/version.rb +3 -0
  33. data/test/commands/generator_test.rb +147 -0
  34. data/test/errors_test.rb +24 -0
  35. data/test/extensions/instruments_test.rb +34 -0
  36. data/test/log_test.rb +27 -0
  37. data/test/middleware/cors_test.rb +42 -0
  38. data/test/middleware/request_id_test.rb +28 -0
  39. data/test/middleware/request_store_test.rb +25 -0
  40. data/test/middleware/rescue_errors_test.rb +41 -0
  41. data/test/middleware/timeout_test.rb +32 -0
  42. data/test/middleware/versioning_test.rb +63 -0
  43. data/test/request_store_test.rb +25 -0
  44. data/test/router_test.rb +39 -0
  45. data/test/test_helper.rb +18 -0
  46. metadata +252 -0
@@ -0,0 +1,49 @@
1
+ module Endpoints
2
+ class <%= plural_class_name %> < Base
3
+ namespace "<%= url_path %>" do
4
+ before do
5
+ content_type :json
6
+ end
7
+
8
+ get do
9
+ MultiJson.encode <%= singular_class_name %>.all.map { |x| serialize(x) }
10
+ end
11
+
12
+ post do
13
+ # warning: not safe
14
+ <%= field_name %> = <%= singular_class_name %>.new(params)
15
+ <%= field_name %>.save
16
+ status 201
17
+ MultiJson.encode serialize(<%= field_name %>)
18
+ end
19
+
20
+ get "/:id" do |id|
21
+ <%= field_name %> = <%= singular_class_name %>.first(uuid: id) || halt(404)
22
+ MultiJson.encode serialize(<%= field_name %>)
23
+ end
24
+
25
+ patch "/:id" do |id|
26
+ <%= field_name %> = <%= singular_class_name %>.first(uuid: id) || halt(404)
27
+ # warning: not safe
28
+ #<%= field_name %>.update(params)
29
+ MultiJson.encode serialize(<%= field_name %>)
30
+ end
31
+
32
+ delete "/:id" do |id|
33
+ <%= field_name %> = <%= singular_class_name %>.first(uuid: id) || halt(404)
34
+ <%= field_name %>.destroy
35
+ MultiJson.encode serialize(<%= field_name %>)
36
+ end
37
+
38
+ private
39
+
40
+ def serialize(data)
41
+ {
42
+ created_at: data.created_at.try(:iso8601),
43
+ id: data.uuid,
44
+ updated_at: data.updated_at.try(:iso8601),
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ describe Endpoints::<%= plural_class_name %> do
4
+ include Committee::Test::Methods
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ Routes
9
+ end
10
+
11
+ def schema_path
12
+ App.root + "/docs/schema.json"
13
+ end
14
+
15
+ before do
16
+ @<%= field_name %> = <%= singular_class_name %>.create
17
+
18
+ # temporarily touch #updated_at until we can fix prmd
19
+ @<%= field_name %>.updated_at
20
+ @<%= field_name %>.save
21
+ end
22
+
23
+ it "GET <%= url_path %>" do
24
+ get "<%= url_path %>"
25
+ last_response.status.should eq(200)
26
+ assert_schema_conform
27
+ end
28
+
29
+ =begin
30
+ it "POST <%= url_path %>" do
31
+ post "<%= url_path %>", MultiJson.encode({})
32
+ last_response.status.should eq(201)
33
+ assert_schema_conform
34
+ end
35
+ =end
36
+
37
+ it "GET <%= url_path %>/:id" do
38
+ get "<%= url_path %>/#{@<%= field_name %>.uuid}"
39
+ last_response.status.should eq(200)
40
+ assert_schema_conform
41
+ end
42
+
43
+ it "PATCH <%= url_path %>/:id" do
44
+ header "Content-Type", "application/json"
45
+ patch "<%= url_path %>/#{@<%= field_name %>.uuid}", MultiJson.encode({})
46
+ last_response.status.should eq(200)
47
+ assert_schema_conform
48
+ end
49
+
50
+ it "GET <%= url_path %>/:id" do
51
+ delete "<%= url_path %>/#{@<%= field_name %>.uuid}"
52
+ last_response.status.should eq(200)
53
+ assert_schema_conform
54
+ end
55
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+ describe Endpoints::<%= plural_class_name %> do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Endpoints::<%= plural_class_name %>
8
+ end
9
+
10
+ describe "GET <%= url_path %>" do
11
+ it "succeeds" do
12
+ get "<%= url_path %>"
13
+ last_response.status.should eq(200)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ <%
2
+ modules = plural_class_name.split("::")
3
+ modules[0] = "Mediators::#{modules[0]}"
4
+ ident = ""
5
+ %>
6
+ <% modules[0..-2].each do |m| %>
7
+ <%= "#{ident}module #{m}\n" %>
8
+ <% ident << " " %>
9
+ <% end %>
10
+ <%= ident %>class <%= modules.last %> < Mediators::Base
11
+ <%= ident %> def initialize(args)
12
+ <%= ident %>
13
+ <%= ident %> end
14
+
15
+ <%= ident %> def call
16
+ <%= ident %>
17
+ <%= ident %> end
18
+ <%= ident %>end
19
+ <% while ident.size > 0 do %>
20
+ <% ident.chop!.chop! %>
21
+ <%= "#{ident}end\n" %>
22
+ <% end %>
@@ -0,0 +1,5 @@
1
+ require "spec_helper"
2
+
3
+ describe Mediators::<%= plural_class_name %> do
4
+
5
+ end
@@ -0,0 +1,9 @@
1
+ Sequel.migration do
2
+ up do
3
+
4
+ end
5
+
6
+ down do
7
+
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class <%= singular_class_name %> < Sequel::Model
2
+
3
+ plugin :timestamps
4
+
5
+ end
@@ -0,0 +1,10 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:<%= table_name %>) do
4
+ uuid :uuid, default: Sequel.function(:uuid_generate_v4), primary_key: true
5
+ timestamptz :created_at, default: Sequel.function(:now), null: false
6
+ timestamptz :updated_at
7
+ timestamptz :deleted_at
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ require "spec_helper"
2
+
3
+ describe <%= singular_class_name %> do
4
+
5
+ end
@@ -0,0 +1,31 @@
1
+ module Pliny
2
+ module Utils
3
+ def self.parse_env(file)
4
+ env = {}
5
+ File.open(file).each do |line|
6
+ line = line.gsub(/#.*$/, '').strip
7
+ next if line.empty?
8
+ var, value = line.split("=", 2)
9
+ value.gsub!(/^['"](.*)['"]$/, '\1')
10
+ env[var] = value
11
+ end
12
+ env
13
+ end
14
+
15
+ # Requires an entire directory of source files in a stable way so that file
16
+ # hierarchy is respected for load order.
17
+ def self.require_glob(path)
18
+ files = Dir[path].sort_by do |file|
19
+ [file.count("/"), file]
20
+ end
21
+
22
+ files.each do |file|
23
+ require file
24
+ end
25
+ end
26
+
27
+ class << self
28
+ alias :require_relative_glob :require_glob
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Pliny
2
+ VERSION = "0.0.1.pre"
3
+ end
@@ -0,0 +1,147 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Commands::Generator do
4
+ before do
5
+ @gen = Pliny::Commands::Generator.new({}, StringIO.new)
6
+ end
7
+
8
+ describe "#plural_class_name" do
9
+ it "builds a class name for a model" do
10
+ @gen.args = ["model", "resource_histories"]
11
+ assert_equal "ResourceHistories", @gen.plural_class_name
12
+ end
13
+ end
14
+
15
+ describe "#singular_class_name" do
16
+ it "builds a class name for an endpoint" do
17
+ @gen.args = ["model", "resource_histories"]
18
+ assert_equal "ResourceHistory", @gen.singular_class_name
19
+ end
20
+ end
21
+
22
+ describe "#table_name" do
23
+ it "uses the plural form" do
24
+ @gen.args = ["model", "resource_history"]
25
+ assert_equal "resource_histories", @gen.table_name
26
+ end
27
+ end
28
+
29
+ describe "#run!" do
30
+ before do
31
+ FileUtils.mkdir_p("/tmp/plinytest")
32
+ Dir.chdir("/tmp/plinytest")
33
+ Timecop.freeze(@t=Time.now)
34
+ end
35
+
36
+ after do
37
+ FileUtils.rmdir("/tmp/plinytest")
38
+ Timecop.return
39
+ end
40
+
41
+ describe "generating endpoints" do
42
+ before do
43
+ @gen.args = ["endpoint", "artists"]
44
+ @gen.run!
45
+ end
46
+
47
+ it "creates a new endpoint module" do
48
+ assert File.exists?("lib/endpoints/artists.rb")
49
+ end
50
+
51
+ it "creates an endpoint test" do
52
+ assert File.exists?("spec/endpoints/artists_spec.rb")
53
+ end
54
+
55
+ it "creates an endpoint acceptance test" do
56
+ assert File.exists?("spec/acceptance/artists_spec.rb")
57
+ end
58
+ end
59
+
60
+ describe "generating mediators" do
61
+ before do
62
+ @gen.args = ["mediator", "artists/creator"]
63
+ @gen.run!
64
+ end
65
+
66
+ it "creates a new endpoint module" do
67
+ assert File.exists?("lib/mediators/artists/creator.rb")
68
+ end
69
+
70
+ it "creates a test" do
71
+ assert File.exists?("spec/mediators/artists/creator_spec.rb")
72
+ end
73
+ end
74
+
75
+ describe "generating models" do
76
+ before do
77
+ @gen.args = ["model", "artists"]
78
+ @gen.run!
79
+ end
80
+
81
+ it "creates a migration" do
82
+ assert File.exists?("db/migrate/#{@t.to_i}_create_artists.rb")
83
+ end
84
+
85
+ it "creates the actual model" do
86
+ assert File.exists?("lib/models/artist.rb")
87
+ end
88
+
89
+ it "creates a test" do
90
+ assert File.exists?("spec/models/artist_spec.rb")
91
+ end
92
+ end
93
+
94
+ describe "generating scaffolds" do
95
+ before do
96
+ @gen.args = ["scaffold", "artist"]
97
+ @gen.run!
98
+ end
99
+
100
+ it "creates a new endpoint module" do
101
+ assert File.exists?("lib/endpoints/artists.rb")
102
+ end
103
+
104
+ it "creates an endpoint test" do
105
+ assert File.exists?("spec/endpoints/artists_spec.rb")
106
+ end
107
+
108
+ it "creates an endpoint acceptance test" do
109
+ assert File.exists?("spec/acceptance/artists_spec.rb")
110
+ end
111
+
112
+ it "creates a migration" do
113
+ assert File.exists?("db/migrate/#{@t.to_i}_create_artists.rb")
114
+ end
115
+
116
+ it "creates the actual model" do
117
+ assert File.exists?("lib/models/artist.rb")
118
+ end
119
+
120
+ it "creates a test" do
121
+ assert File.exists?("spec/models/artist_spec.rb")
122
+ end
123
+
124
+ it "creates a schema" do
125
+ assert File.exists?("docs/schema/schemata/artist.yaml")
126
+ end
127
+ end
128
+
129
+ describe "generating schemas" do
130
+ before do
131
+ @gen.args = ["schema", "artist"]
132
+ @gen.run!
133
+ end
134
+
135
+ it "creates a schema" do
136
+ assert File.exists?("docs/schema/schemata/artist.yaml")
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "#url_path" do
142
+ it "builds a URL path" do
143
+ @gen.args = ["endpoint", "resource_history"]
144
+ assert_equal "/resource-histories", @gen.url_path
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,24 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Errors do
4
+ it "includes a general error that requires an identifier" do
5
+ e = Pliny::Errors::Error.new("General error.", :general_error)
6
+ assert_equal "General error.", e.message
7
+ assert_equal :general_error, e.id
8
+ end
9
+
10
+ it "includes an HTTP error that will take generic parameters" do
11
+ e = Pliny::Errors::HTTPStatusError.new(
12
+ "Custom HTTP error.", :custom_http_error, 499)
13
+ assert_equal "Custom HTTP error.", e.message
14
+ assert_equal :custom_http_error, e.id
15
+ assert_equal 499, e.status
16
+ end
17
+
18
+ it "includes pre-defined HTTP error templates" do
19
+ e = Pliny::Errors::NotFound.new
20
+ assert_equal "Not found.", e.message
21
+ assert_equal :not_found, e.id
22
+ assert_equal 404, e.status
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Extensions::Instruments do
4
+ def app
5
+ Rack::Builder.new do
6
+ run Sinatra.new {
7
+ register Pliny::Extensions::Instruments
8
+
9
+ get "/apps/:id" do
10
+ status 201
11
+ "hi"
12
+ end
13
+ }
14
+ end
15
+ end
16
+
17
+ it "performs logging" do
18
+ mock(Pliny).log(hash_including(
19
+ instrumentation: true,
20
+ at: "start",
21
+ method: "GET",
22
+ path: "/apps/123",
23
+ ))
24
+ mock(Pliny).log(hash_including(
25
+ instrumentation: true,
26
+ at: "finish",
27
+ method: "GET",
28
+ path: "/apps/123",
29
+ route_signature: "/apps/:id",
30
+ status: 201
31
+ ))
32
+ get "/apps/123"
33
+ end
34
+ end
data/test/log_test.rb ADDED
@@ -0,0 +1,27 @@
1
+ require "test_helper"
2
+
3
+ describe Pliny::Log do
4
+ before do
5
+ @io = StringIO.new
6
+ Pliny.stdout = @io
7
+ stub(@io).puts
8
+ end
9
+
10
+ it "logs in structured format" do
11
+ mock(@io).puts "foo=bar baz=42"
12
+ Pliny.log(foo: "bar", baz: 42)
13
+ end
14
+
15
+ it "supports blocks to log stages and elapsed" do
16
+ mock(@io).puts "foo=bar at=start"
17
+ mock(@io).puts "foo=bar at=finish elapsed=0.000"
18
+ Pliny.log(foo: "bar") do
19
+ end
20
+ end
21
+
22
+ it "merges context from RequestStore" do
23
+ Pliny::RequestStore.store[:log_context] = { app: "pliny" }
24
+ mock(@io).puts "app=pliny foo=bar"
25
+ Pliny.log(foo: "bar")
26
+ end
27
+ end