pliny 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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({})