pliny 0.2.1 → 0.3.0

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 (49) hide show
  1. checksums.yaml +14 -6
  2. data/README.md +59 -11
  3. data/bin/pliny-generate +2 -14
  4. data/bin/pliny-new +1 -1
  5. data/lib/pliny.rb +1 -2
  6. data/lib/pliny/commands/creator.rb +10 -11
  7. data/lib/pliny/commands/generator.rb +50 -199
  8. data/lib/pliny/commands/generator/base.rb +59 -0
  9. data/lib/pliny/commands/generator/endpoint.rb +44 -0
  10. data/lib/pliny/commands/generator/mediator.rb +21 -0
  11. data/lib/pliny/commands/generator/migration.rb +13 -0
  12. data/lib/pliny/commands/generator/model.rb +30 -0
  13. data/lib/pliny/commands/generator/schema.rb +24 -0
  14. data/lib/pliny/commands/generator/serializer.rb +21 -0
  15. data/lib/pliny/config_helpers.rb +52 -0
  16. data/lib/pliny/helpers/encode.rb +7 -0
  17. data/lib/pliny/log.rb +18 -1
  18. data/lib/pliny/middleware/versioning.rb +3 -3
  19. data/lib/pliny/tasks/db.rake +21 -7
  20. data/lib/pliny/tasks/schema.rake +2 -2
  21. data/lib/pliny/templates/endpoint.erb +5 -5
  22. data/lib/pliny/templates/endpoint_acceptance_test.erb +1 -1
  23. data/lib/pliny/templates/endpoint_scaffold.erb +5 -5
  24. data/lib/pliny/templates/endpoint_scaffold_acceptance_test.erb +1 -1
  25. data/lib/pliny/templates/endpoint_test.erb +1 -0
  26. data/lib/pliny/templates/migration.erb +0 -2
  27. data/lib/pliny/templates/model.erb +0 -2
  28. data/lib/pliny/templates/model_test.erb +0 -1
  29. data/lib/pliny/templates/serializer_test.erb +0 -1
  30. data/lib/pliny/version.rb +1 -1
  31. data/template/.env.sample +2 -1
  32. data/template/README.md +1 -1
  33. data/template/bin/console +1 -3
  34. data/template/bin/run +1 -3
  35. data/template/config.ru +1 -4
  36. data/template/config/config.rb +19 -25
  37. data/template/config/initializers/rollbar.rb +1 -0
  38. data/template/config/puma.rb +2 -2
  39. data/template/db/schema.sql +1 -2
  40. data/template/lib/application.rb +4 -0
  41. data/template/lib/endpoints/base.rb +1 -0
  42. data/template/lib/routes.rb +1 -1
  43. data/template/lib/serializers/base.rb +8 -1
  44. data/test/commands/creator_test.rb +1 -0
  45. data/test/commands/generator/base_test.rb +103 -0
  46. data/test/commands/generator/endpoint_test.rb +15 -0
  47. data/test/commands/generator_test.rb +95 -134
  48. data/test/log_test.rb +16 -0
  49. metadata +100 -69
@@ -0,0 +1,59 @@
1
+ require 'active_support/inflector'
2
+ require 'ostruct'
3
+ require 'erb'
4
+ require 'fileutils'
5
+ require 'pliny'
6
+
7
+ module Pliny::Commands
8
+ class Generator
9
+ class Base
10
+ attr_reader :name, :stream, :options
11
+
12
+ def initialize(name, options = {}, stream = $stdout)
13
+ @name = name
14
+ @options = options
15
+ @stream = stream
16
+ end
17
+
18
+ def singular_class_name
19
+ name.gsub(/-/, '_').singularize.camelize
20
+ end
21
+
22
+ def plural_class_name
23
+ name.gsub(/-/, '_').pluralize.camelize
24
+ end
25
+
26
+ def field_name
27
+ name.tableize.singularize
28
+ end
29
+
30
+ def pluralized_file_name
31
+ name.tableize
32
+ end
33
+
34
+ def table_name
35
+ name.tableize.gsub('/', '_')
36
+ end
37
+
38
+ def display(msg)
39
+ stream.puts msg
40
+ end
41
+
42
+ def render_template(template_file, destination_path, vars = {})
43
+ template_path = File.dirname(__FILE__) + "/../../templates/#{template_file}"
44
+ template = ERB.new(File.read(template_path), 0, '>')
45
+ context = OpenStruct.new(vars)
46
+ write_file(destination_path) do
47
+ template.result(context.instance_eval { binding })
48
+ end
49
+ end
50
+
51
+ def write_file(destination_path)
52
+ FileUtils.mkdir_p(File.dirname(destination_path))
53
+ File.open(destination_path, 'w') do |f|
54
+ f.puts yield
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,44 @@
1
+ require_relative 'base'
2
+
3
+ module Pliny::Commands
4
+ class Generator
5
+ class Endpoint < Base
6
+ def create
7
+ endpoint = "./lib/endpoints/#{pluralized_file_name}.rb"
8
+ template = options[:scaffold] ? 'endpoint_scaffold.erb' : 'endpoint.erb'
9
+ render_template(template, endpoint,
10
+ plural_class_name: plural_class_name,
11
+ singular_class_name: singular_class_name,
12
+ field_name: field_name,
13
+ url_path: url_path)
14
+ display "created endpoint file #{endpoint}"
15
+ display 'add the following to lib/routes.rb:'
16
+ display " mount Endpoints::#{plural_class_name}"
17
+ end
18
+
19
+ def create_test
20
+ test = "./spec/endpoints/#{pluralized_file_name}_spec.rb"
21
+ render_template('endpoint_test.erb', test,
22
+ plural_class_name: plural_class_name,
23
+ singular_class_name: singular_class_name,
24
+ url_path: url_path)
25
+ display "created test #{test}"
26
+ end
27
+
28
+ def create_acceptance_test
29
+ test = "./spec/acceptance/#{pluralized_file_name}_spec.rb"
30
+ template = options[:scaffold] ? 'endpoint_scaffold_acceptance_test.erb' : 'endpoint_acceptance_test.erb'
31
+ render_template(template, test,
32
+ plural_class_name: plural_class_name,
33
+ field_name: field_name,
34
+ singular_class_name: singular_class_name,
35
+ url_path: url_path)
36
+ display "created test #{test}"
37
+ end
38
+
39
+ def url_path
40
+ '/' + name.pluralize.gsub(/_/, '-')
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'base'
2
+
3
+ module Pliny::Commands
4
+ class Generator
5
+ class Mediator < Base
6
+ def create
7
+ mediator = "./lib/mediators/#{field_name}.rb"
8
+ render_template('mediator.erb', mediator,
9
+ singular_class_name: singular_class_name)
10
+ display "created mediator file #{mediator}"
11
+ end
12
+
13
+ def create_test
14
+ test = "./spec/mediators/#{field_name}_spec.rb"
15
+ render_template('mediator_test.erb', test,
16
+ singular_class_name: singular_class_name)
17
+ display "created test #{test}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'base'
2
+
3
+ module Pliny::Commands
4
+ class Generator
5
+ class Migration < Base
6
+ def create
7
+ migration = "./db/migrate/#{Time.now.to_i}_#{name}.rb"
8
+ render_template('migration.erb', migration)
9
+ display "created migration #{migration}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'base'
2
+
3
+ module Pliny::Commands
4
+ class Generator
5
+ class Model < Base
6
+ def create
7
+ model = "./lib/models/#{field_name}.rb"
8
+ render_template('model.erb', model,
9
+ singular_class_name: singular_class_name,
10
+ paranoid: options[:paranoid])
11
+ display "created model file #{model}"
12
+ end
13
+
14
+ def create_migration
15
+ migration = "./db/migrate/#{Time.now.to_i}_create_#{table_name}.rb"
16
+ render_template('model_migration.erb', migration,
17
+ table_name: table_name,
18
+ paranoid: options[:paranoid])
19
+ display "created migration #{migration}"
20
+ end
21
+
22
+ def create_test
23
+ test = "./spec/models/#{field_name}_spec.rb"
24
+ render_template('model_test.erb', test,
25
+ singular_class_name: singular_class_name)
26
+ display "created test #{test}"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'base'
2
+ require 'prmd'
3
+
4
+ module Pliny::Commands
5
+ class Generator
6
+ class Schema < Base
7
+ def create
8
+ schema = "./docs/schema/schemata/#{field_name}.yaml"
9
+ write_file(schema) do
10
+ Prmd.init(name.singularize, yaml: true)
11
+ end
12
+ display "created schema file #{schema}"
13
+ end
14
+
15
+ def rebuild
16
+ schemata = './docs/schema.json'
17
+ write_file(schemata) do
18
+ Prmd.combine('./docs/schema/schemata', meta: './docs/schema/meta.json')
19
+ end
20
+ display "rebuilt #{schemata}"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'base'
2
+
3
+ module Pliny::Commands
4
+ class Generator
5
+ class Serializer < Base
6
+ def create
7
+ serializer = "./lib/serializers/#{name}.rb"
8
+ render_template('serializer.erb', serializer,
9
+ singular_class_name: singular_class_name)
10
+ display "created serializer file #{serializer}"
11
+ end
12
+
13
+ def create_test
14
+ test = "./spec/serializers/#{name}_spec.rb"
15
+ render_template('serializer_test.erb', test,
16
+ singular_class_name: singular_class_name)
17
+ display "created test #{test}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,55 @@
1
1
  module Pliny
2
+ module CastingConfigHelpers
3
+ def mandatory(name, method=nil)
4
+ value = cast(ENV.fetch(name.to_s.upcase), method)
5
+ create(name, value)
6
+ end
7
+
8
+ def optional(name, method=nil)
9
+ value = cast(ENV[name.to_s.upcase], method)
10
+ create(name, value)
11
+ end
12
+
13
+ def override(name, default, method=nil)
14
+ value = cast(ENV.fetch(name.to_s.upcase, default), method)
15
+ create(name, value)
16
+ end
17
+
18
+ def int
19
+ ->(v) { v.to_i }
20
+ end
21
+
22
+ def float
23
+ ->(v) { v.to_f }
24
+ end
25
+
26
+ def bool
27
+ ->(v) { v.to_s=='true'}
28
+ end
29
+
30
+ def string
31
+ nil
32
+ end
33
+
34
+ def symbol
35
+ ->(v) { v.to_sym }
36
+ end
37
+
38
+ private
39
+
40
+ def cast(value, method)
41
+ method ? method.call(value) : value
42
+ end
43
+
44
+ def create(name, value)
45
+ instance_variable_set(:"@#{name}", value)
46
+ instance_eval "def #{name}; @#{name} end", __FILE__, __LINE__
47
+ if value.kind_of?(TrueClass) || value.kind_of?(FalseClass) || value.kind_of?(NilClass)
48
+ instance_eval "def #{name}?; !!@#{name} end", __FILE__, __LINE__
49
+ end
50
+ end
51
+ end
52
+
2
53
  module ConfigHelpers
3
54
  def optional(*attrs)
4
55
  attrs.each do |attr|
@@ -28,6 +79,7 @@ module Pliny
28
79
  end
29
80
  end
30
81
  end
82
+
31
83
  end
32
84
 
33
85
  # Supress the "use RbConfig instead" warning.
@@ -0,0 +1,7 @@
1
+ module Pliny::Helpers
2
+ module Encode
3
+ def encode(object)
4
+ MultiJson.encode(object, pretty: params[:pretty] == 'true' || Config.pretty_json)
5
+ end
6
+ end
7
+ end
@@ -1,10 +1,19 @@
1
1
  module Pliny
2
2
  module Log
3
3
  def log(data, &block)
4
- data = log_context.merge(data)
4
+ data = log_context.merge(local_context.merge(data))
5
5
  log_to_stream(stdout || $stdout, data, &block)
6
6
  end
7
7
 
8
+ def context(data, &block)
9
+ old = local_context
10
+ self.local_context = old.merge(data)
11
+ res = block.call
12
+ ensure
13
+ local_context = old
14
+ res
15
+ end
16
+
8
17
  def stdout=(stream)
9
18
  @stdout = stream
10
19
  end
@@ -15,6 +24,14 @@ module Pliny
15
24
 
16
25
  private
17
26
 
27
+ def local_context
28
+ RequestStore.store[:local_context] ||= {}
29
+ end
30
+
31
+ def local_context=(h)
32
+ RequestStore.store[:local_context] = h
33
+ end
34
+
18
35
  def log_context
19
36
  RequestStore.store[:log_context] || {}
20
37
  end
@@ -22,7 +22,7 @@ module Pliny::Middleware
22
22
  version = nil
23
23
  media_types.map! do |media_type|
24
24
  if accept_headers.include?(media_type.format)
25
- if !media_type.params["version"]
25
+ unless media_type.params['version']
26
26
  error = { id: :bad_version, message: <<-eos }
27
27
  Please specify a version along with the MIME type. For example, `Accept: application/vnd.#{@app_name}+json; version=1`.
28
28
  eos
@@ -30,7 +30,7 @@ Please specify a version along with the MIME type. For example, `Accept: applica
30
30
  [MultiJson.encode(error)]]
31
31
  end
32
32
 
33
- if !version
33
+ unless version
34
34
  version = media_type.params["version"]
35
35
  end
36
36
 
@@ -41,7 +41,7 @@ Please specify a version along with the MIME type. For example, `Accept: applica
41
41
  end
42
42
  media_type.to_s
43
43
  end
44
- env["HTTP_ACCEPT"] = media_types.map(&:to_s).join(", ")
44
+ env['HTTP_ACCEPT'] = media_types.join(', ')
45
45
 
46
46
  version ||= @default
47
47
  set_api_version(env, version)
@@ -78,19 +78,33 @@ namespace :db do
78
78
  namespace :schema do
79
79
  desc "Load the database schema"
80
80
  task :load do
81
- schema = File.read("./db/schema.sql")
82
- database_urls.each do |database_url|
83
- db = Sequel.connect(database_url)
84
- db.run(schema)
85
- puts "Loaded `#{name_from_uri(database_url)}`"
81
+ if File.exists?("./db/schema.sql")
82
+ schema = File.read("./db/schema.sql")
83
+ database_urls.each do |database_url|
84
+ db = Sequel.connect(database_url)
85
+ db.run(schema)
86
+ puts "Loaded `#{name_from_uri(database_url)}`"
87
+ end
88
+ else
89
+ puts "Skipped schema load, schema.sql not present"
86
90
  end
87
91
  end
88
92
 
89
93
  desc "Dump the database schema"
90
94
  task :dump do
95
+ file = File.join("db", "schema.sql")
91
96
  database_url = database_urls.first
92
- `pg_dump -i -s -x -O -f ./db/schema.sql #{database_url}`
93
- puts "Dumped `#{name_from_uri(database_url)}` to db/schema.sql"
97
+ `pg_dump -i -s -x -O -f #{file} #{database_url}`
98
+
99
+ schema = File.read(file)
100
+ # filter all COMMENT ON EXTENSION, only owners and the db
101
+ # superuser can execute these, making it impossible to just
102
+ # replay such statements in certain production databases
103
+ schema.gsub!(/^COMMENT ON EXTENSION.*\n/, "")
104
+
105
+ File.open(file, "w") { |f| f.puts schema }
106
+
107
+ puts "Dumped `#{name_from_uri(database_url)}` to #{file}"
94
108
  end
95
109
 
96
110
  desc "Merges migrations into schema and removes them"
@@ -1,5 +1,5 @@
1
1
  desc "Rebuild schema.json"
2
2
  task :schema do
3
- require 'pliny'
4
- Pliny::Commands::Generator.new.rebuild_schema
3
+ require 'pliny/commands/generator/schema'
4
+ Pliny::Commands::Generator::Schema.new(nil).rebuild
5
5
  end
@@ -6,24 +6,24 @@ module Endpoints
6
6
  end
7
7
 
8
8
  get do
9
- "[]"
9
+ encode []
10
10
  end
11
11
 
12
12
  post do
13
13
  status 201
14
- "{}"
14
+ encode {}
15
15
  end
16
16
 
17
17
  get "/:id" do
18
- "{}"
18
+ encode {}
19
19
  end
20
20
 
21
21
  patch "/:id" do |id|
22
- "{}"
22
+ encode {}
23
23
  end
24
24
 
25
25
  delete "/:id" do |id|
26
- "{}"
26
+ encode {}
27
27
  end
28
28
  end
29
29
  end
@@ -20,7 +20,7 @@ describe Endpoints::<%= plural_class_name %> do
20
20
  end
21
21
  end
22
22
 
23
- describe 'POST <%= url_path %>/:id' do
23
+ describe 'POST <%= url_path %>' do
24
24
  it 'returns correct status code and conforms to schema' do
25
25
  header "Content-Type", "application/json"
26
26
  post '<%= url_path %>', MultiJson.encode({})