pliny 0.0.1.pre

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 (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