gris 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +1 -0
  5. data/.rubocop_todo.yml +67 -0
  6. data/.ruby-gemset +1 -0
  7. data/.ruby-version +1 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +203 -0
  10. data/Guardfile +82 -0
  11. data/MIT-LICENSE +20 -0
  12. data/README.md +7 -0
  13. data/Rakefile +11 -0
  14. data/bin/gris +5 -0
  15. data/gris.gemspec +40 -0
  16. data/lib/gris.rb +37 -0
  17. data/lib/gris/cli.rb +71 -0
  18. data/lib/gris/deprecations.rb +9 -0
  19. data/lib/gris/deprecations/gris_setup.rb +14 -0
  20. data/lib/gris/generators.rb +3 -0
  21. data/lib/gris/generators/api_generator.rb +31 -0
  22. data/lib/gris/generators/migration_generator.rb +228 -0
  23. data/lib/gris/generators/scaffold_generator.rb +21 -0
  24. data/lib/gris/generators/templates/api/app/apis/%name_tableize%_endpoint.rb.tt +65 -0
  25. data/lib/gris/generators/templates/api/app/models/%name_underscore%.rb.tt +2 -0
  26. data/lib/gris/generators/templates/api/app/presenters/%name_tableize%_presenter.rb.tt +5 -0
  27. data/lib/gris/generators/templates/api/app/presenters/%name_underscore%_presenter.rb.tt +10 -0
  28. data/lib/gris/generators/templates/api/spec/apis/%name_tableize%_endpoint_spec.rb.tt +70 -0
  29. data/lib/gris/generators/templates/api/spec/fabricators/%name_tableize%_fabricator.rb.tt +2 -0
  30. data/lib/gris/generators/templates/api/spec/models/%name_underscore%_spec.rb.tt +7 -0
  31. data/lib/gris/generators/templates/create_table_migration/%migration_filename%.rb.tt +19 -0
  32. data/lib/gris/generators/templates/migration/%migration_filename%.rb.tt +39 -0
  33. data/lib/gris/generators/templates/scaffold/.Procfile.tt +1 -0
  34. data/lib/gris/generators/templates/scaffold/.env.test.tt +11 -0
  35. data/lib/gris/generators/templates/scaffold/.env.tt +10 -0
  36. data/lib/gris/generators/templates/scaffold/.gitignore.tt +11 -0
  37. data/lib/gris/generators/templates/scaffold/.rspec.tt +3 -0
  38. data/lib/gris/generators/templates/scaffold/.rubocop.yml +18 -0
  39. data/lib/gris/generators/templates/scaffold/.ruby-gemset.tt +1 -0
  40. data/lib/gris/generators/templates/scaffold/.ruby-version.tt +1 -0
  41. data/lib/gris/generators/templates/scaffold/Gemfile.tt +33 -0
  42. data/lib/gris/generators/templates/scaffold/README.md.tt +3 -0
  43. data/lib/gris/generators/templates/scaffold/Rakefile +9 -0
  44. data/lib/gris/generators/templates/scaffold/app.rb +57 -0
  45. data/lib/gris/generators/templates/scaffold/app/apis/application_endpoint.rb +9 -0
  46. data/lib/gris/generators/templates/scaffold/app/presenters/root_presenter.rb +20 -0
  47. data/lib/gris/generators/templates/scaffold/config.ru.tt +6 -0
  48. data/lib/gris/generators/templates/scaffold/config/database.yml.tt +19 -0
  49. data/lib/gris/generators/templates/scaffold/config/initializers/active_record.rb +4 -0
  50. data/lib/gris/generators/templates/scaffold/db/schema.rb +11 -0
  51. data/lib/gris/generators/templates/scaffold/public/errors/404.html +11 -0
  52. data/lib/gris/generators/templates/scaffold/public/errors/500.html +11 -0
  53. data/lib/gris/generators/templates/scaffold/spec/apis/cors_spec.rb +31 -0
  54. data/lib/gris/generators/templates/scaffold/spec/spec_helper.rb +26 -0
  55. data/lib/gris/generators/templates/scaffold/spec/support/app_helper.rb +3 -0
  56. data/lib/gris/grape_extensions/crud_helpers.rb +26 -0
  57. data/lib/gris/identity.rb +50 -0
  58. data/lib/gris/middleware/app_monitor.rb +17 -0
  59. data/lib/gris/output_formatters/paginated_presenter.rb +41 -0
  60. data/lib/gris/output_formatters/presenter.rb +15 -0
  61. data/lib/gris/rspec_extensions/response_helpers.rb +57 -0
  62. data/lib/gris/setup.rb +17 -0
  63. data/lib/gris/version.rb +38 -0
  64. data/lib/tasks/db.rake +81 -0
  65. data/lib/tasks/routes.rake +11 -0
  66. data/spec/generators/api_generator_spec.rb +76 -0
  67. data/spec/generators/migration_generator_spec.rb +96 -0
  68. data/spec/generators/scaffold_generator_spec.rb +119 -0
  69. data/spec/grape_extensions/crud_helpers_spec.rb +5 -0
  70. data/spec/identity_spec.rb +86 -0
  71. data/spec/spec_helper.rb +10 -0
  72. data/spec/support/spec_generators_helper.rb +11 -0
  73. data/spec/version_spec.rb +40 -0
  74. metadata +426 -0
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/gris/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'gris'
6
+ s.version = Gris::VERSION
7
+ s.summary = 'A simple api microservice generator.'
8
+ s.description = 'Gris is a generator for Grape, Roar, Hypermedia, PG API apps.'
9
+ s.email = 'email@dylanfareed.com'
10
+ s.homepage = 'http://github.com/dylanfareed/gris/'
11
+ s.authors = ['Dylan Fareed']
12
+ s.licenses = 'MIT-LICENSE'
13
+
14
+ s.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
15
+ s.executables << 'gris'
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+ s.name = 'gris'
18
+ s.require_paths = ['lib']
19
+ s.required_ruby_version = '>= 2.1.5'
20
+
21
+ s.add_runtime_dependency 'thor', '~> 0.19', '>= 0.19.1'
22
+ s.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.0'
23
+ s.add_runtime_dependency 'rake', '~> 10.4', '>= 10.4.2'
24
+ s.add_runtime_dependency 'git', '~> 1.2', '>= 1.2.8'
25
+
26
+ s.add_dependency 'logging'
27
+ s.add_dependency 'dotenv', '~> 1.0.2', '>= 1.0.2'
28
+ s.add_dependency 'grape', '~> 0.10.1', '>= 0.10.1'
29
+ s.add_dependency 'grape-roar', '~> 0.3.0', '>= 0.3.0'
30
+
31
+ s.add_dependency 'grape-swagger', '~> 0.9.0', '>= 0.9.0'
32
+ s.add_dependency 'roar', '~> 1.0.0', '>= 1.0.0'
33
+ s.add_dependency 'racksh', '~> 1.0'
34
+
35
+ s.add_development_dependency 'bundler', '~> 1.7.10', '>= 1.7.10'
36
+ s.add_development_dependency 'rspec', '~> 3.1', '>= 3.1.0'
37
+ s.add_development_dependency 'rubocop', '~> 0.28', '>= 0.28.0'
38
+ s.add_development_dependency 'activerecord', '~> 4.2', '>= 4.2.0'
39
+ s.add_development_dependency 'acts_as_fu', '~> 0'
40
+ end
@@ -0,0 +1,37 @@
1
+ # require external libraries
2
+ require 'rake'
3
+ require 'dotenv'
4
+ require 'yaml'
5
+ require 'grape'
6
+ require 'json'
7
+ require 'roar'
8
+ require 'grape-roar'
9
+ require 'roar/representer'
10
+ require 'roar/json'
11
+ require 'roar/json/hal'
12
+
13
+ # require internal files
14
+ require 'gris/deprecations'
15
+ require 'gris/grape_extensions/crud_helpers'
16
+ require 'gris/identity'
17
+ require 'gris/middleware/app_monitor'
18
+ require 'gris/output_formatters/paginated_presenter'
19
+ require 'gris/output_formatters/presenter'
20
+ require 'gris/setup'
21
+ require 'gris/version'
22
+
23
+ # load rake tasks if Rake installed
24
+ if defined?(Rake)
25
+ load 'tasks/routes.rake'
26
+ load 'tasks/db.rake'
27
+ end
28
+
29
+ module Gris
30
+ class << self
31
+ def initialize
32
+ Gris::Deprecations.initialization_checks
33
+ end
34
+ end
35
+ end
36
+
37
+ Gris.initialize
@@ -0,0 +1,71 @@
1
+ require 'thor'
2
+ require 'gris/generators'
3
+ require 'gris/version'
4
+
5
+ Gris.load_environment if defined?(Dotenv)
6
+
7
+ module Gris
8
+ class CLI
9
+ class Generate < Thor
10
+ register(
11
+ Generators::ApiGenerator,
12
+ 'api',
13
+ 'api <api_name>',
14
+ 'Create a Grape API, Model and Presenter'
15
+ )
16
+
17
+ register(
18
+ Generators::MigrationGenerator,
19
+ 'migration',
20
+ 'migration <migration_name> [field[:type][:index] field[:type][:index]]',
21
+ 'Create a Database Migration'
22
+ )
23
+ end
24
+ end
25
+ class CLI
26
+ class Base < Thor
27
+ desc 'version', 'Returns the Gris version number'
28
+ def version
29
+ say Gris::VERSION
30
+ end
31
+
32
+ desc 'console [environment]', 'Start the Gris console'
33
+ options aliases: 'c'
34
+ def console(environment = nil)
35
+ ENV['RACK_ENV'] = environment || 'development'
36
+
37
+ require 'racksh/init'
38
+
39
+ begin
40
+ require 'pry'
41
+ interpreter = Pry
42
+ rescue LoadError
43
+ require 'irb'
44
+ require 'irb/completion'
45
+ interpreter = IRB
46
+ # IRB uses ARGV and does not expect these arguments.
47
+ ARGV.delete('console')
48
+ ARGV.delete(environment) if environment
49
+ end
50
+
51
+ Rack::Shell.init
52
+
53
+ $0 = "#{$PROGRAM_NAME} console"
54
+ interpreter.start
55
+ end
56
+
57
+ register(
58
+ Generators::ScaffoldGenerator,
59
+ 'new',
60
+ 'new <app_name> [app_path]',
61
+ 'Creates a scaffold for a new Gris service'
62
+ )
63
+
64
+ desc 'generate api <api_name>', 'Create a Grape API, Model and Representer'
65
+ subcommand 'generate api', Gris::CLI::Generate
66
+
67
+ desc 'generate migration <migration_name> [field[:type][:index] field[:type][:index]]', 'Create a Database Migration'
68
+ subcommand 'generate', Gris::CLI::Generate
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,9 @@
1
+ require 'gris/deprecations/gris_setup'
2
+
3
+ module Gris
4
+ class Deprecations
5
+ def self.initialization_checks
6
+ gris_setup_check
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ module Gris
2
+ class Deprecations
3
+ # TODO: Called when Gris appliation loads.
4
+ #
5
+ # Use this method to check that embedding applications
6
+ # follow current established best practices.
7
+ # cf. https://github.com/bellycard/napa/blob/master/lib/napa/deprecations.rb
8
+ # https://github.com/bellycard/napa/blob/master/lib/napa/deprecations/napa_setup.rb
9
+ # https://github.com/bellycard/napa/blob/master/lib/napa/deprecations/application_api.rb
10
+ #
11
+ def self.gris_setup_check
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ require 'gris/generators/api_generator'
2
+ require 'gris/generators/migration_generator'
3
+ require 'gris/generators/scaffold_generator'
@@ -0,0 +1,31 @@
1
+ require 'thor'
2
+ require 'active_support/all'
3
+
4
+ module Gris
5
+ module Generators
6
+ class ApiGenerator < Thor::Group
7
+ include Thor::Actions
8
+ argument :name
9
+
10
+ def name_underscore
11
+ name.underscore
12
+ end
13
+
14
+ def name_tableize
15
+ name.tableize
16
+ end
17
+
18
+ def output_directory
19
+ '.'
20
+ end
21
+
22
+ def api
23
+ self.class.source_root "#{File.dirname(__FILE__)}/templates/api"
24
+ say 'Generating api...'
25
+ directory '.', output_directory
26
+ say 'API files created!', :green
27
+ say 'Note that you will need to mount this new endpoint in your ApplicationEndpoint.', :green
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,228 @@
1
+ require 'thor'
2
+ require 'active_support/all'
3
+
4
+ module Gris
5
+ module Generators
6
+ class MigrationGenerator < Thor::Group
7
+ include Thor::Actions
8
+ # largely ported over, with a few differences, from
9
+ # https://github.com/rails/rails/blob/76883f92374c6395f13c16628e1d87d40b6d2399/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
10
+ argument :migration_name
11
+ argument :attributes, type: :array, default: []
12
+
13
+ attr_reader :migration_action, :join_tables, :table_name
14
+
15
+ def version
16
+ Time.now.utc.strftime('%Y%m%d%H%M%S')
17
+ end
18
+
19
+ def migration_filename
20
+ "#{version}_#{migration_name.underscore}"
21
+ end
22
+
23
+ def output_directory
24
+ './db/migrate'
25
+ end
26
+
27
+ def parse_attributes!
28
+ self.attributes = (attributes || []).map do |attr|
29
+ GeneratedAttribute.parse(attr)
30
+ end
31
+ end
32
+
33
+ def set_local_assigns!
34
+ @migration_template = 'migration'
35
+ filename = migration_name.underscore
36
+ case filename
37
+ when /^(add|remove)_.*_(?:to|from)_(.*)/
38
+ @migration_action = Regexp.last_match[1]
39
+ @table_name = Regexp.last_match[2].pluralize
40
+ when /join_table/
41
+ if attributes.length == 2
42
+ @migration_action = 'join'
43
+ @join_tables = attributes.map(&:plural_name)
44
+
45
+ set_index_names
46
+ end
47
+ when /^create_(.+)/
48
+ @table_name = Regexp.last_match[1].pluralize
49
+ @migration_template = 'create_table_migration'
50
+ end
51
+ end
52
+
53
+ def migration
54
+ self.class.source_root "#{File.dirname(__FILE__)}/templates/#{@migration_template}"
55
+ say 'Generating migration...'
56
+ directory '.', output_directory
57
+ say 'Done!', :green
58
+ end
59
+
60
+ private
61
+
62
+ def attributes_with_index
63
+ attributes.select { |a| !a.reference? && a.has_index? }
64
+ end
65
+
66
+ def set_index_names
67
+ attributes.each_with_index do |attr, i|
68
+ attr.index_name = [attr, attributes[i - 1]].map { |a| index_name_for(a) }
69
+ end
70
+ end
71
+
72
+ def index_name_for(attribute)
73
+ if attribute.foreign_key?
74
+ attribute.name
75
+ else
76
+ attribute.name.singularize.foreign_key
77
+ end.to_sym
78
+ end
79
+ end
80
+
81
+ # directly ported from
82
+ # https://github.com/rails/rails/blob/76883f92374c6395f13c16628e1d87d40b6d2399/railties/lib/rails/generators/generated_attribute.rb
83
+ class GeneratedAttribute # :nodoc:
84
+ INDEX_OPTIONS = %w(index uniq)
85
+ UNIQ_INDEX_OPTIONS = %w(uniq)
86
+
87
+ attr_accessor :name, :type
88
+ attr_reader :attr_options
89
+ attr_writer :index_name
90
+
91
+ class << self
92
+ def parse(column_definition)
93
+ name, type, has_index = column_definition.split(':')
94
+
95
+ # if user provided "name:index" instead of "name:string:index"
96
+ # type should be set blank so GeneratedAttribute's constructor
97
+ # could set it to :string
98
+ has_index, type = type, nil if INDEX_OPTIONS.include?(type)
99
+
100
+ type, attr_options = *parse_type_and_options(type)
101
+ type = type.to_sym if type
102
+
103
+ if type && reference?(type)
104
+ references_index = UNIQ_INDEX_OPTIONS.include?(has_index) ? { unique: true } : true
105
+ attr_options[:index] = references_index
106
+ end
107
+
108
+ new(name, type, has_index, attr_options)
109
+ end
110
+
111
+ def reference?(type)
112
+ [:references, :belongs_to].include? type
113
+ end
114
+
115
+ private
116
+
117
+ # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
118
+ # when declaring options curly brackets should be used
119
+ def parse_type_and_options(type)
120
+ case type
121
+ when /(string|text|binary|integer)\{(\d+)\}/
122
+ return Regexp.last_match[1], limit: Regexp.last_match[2].to_i
123
+ when /decimal\{(\d+)[,.-](\d+)\}/
124
+ return :decimal, precision: Regexp.last_match[1].to_i, scale: Regexp.last_match[2].to_i
125
+ when /(references|belongs_to)\{polymorphic\}/
126
+ return Regexp.last_match[1], polymorphic: true
127
+ else
128
+ return type, {}
129
+ end
130
+ end
131
+ end
132
+
133
+ def initialize(name, type = nil, index_type = false, attr_options = {})
134
+ @name = name
135
+ @type = type || :string
136
+ @has_index = INDEX_OPTIONS.include?(index_type)
137
+ @has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type)
138
+ @attr_options = attr_options
139
+ end
140
+
141
+ def field_type
142
+ @field_type ||= case type
143
+ when :integer then :number_field
144
+ when :float, :decimal then :text_field
145
+ when :time then :time_select
146
+ when :datetime, :timestamp then :datetime_select
147
+ when :date then :date_select
148
+ when :text then :text_area
149
+ when :boolean then :check_box
150
+ else
151
+ :text_field
152
+ end
153
+ end
154
+
155
+ def default
156
+ @default ||= case type
157
+ when :integer then 1
158
+ when :float then 1.5
159
+ when :decimal then '9.99'
160
+ when :datetime, :timestamp, :time then Time.now.to_s(:db)
161
+ when :date then Date.today.to_s(:db)
162
+ when :string then name == 'type' ? '' : 'MyString'
163
+ when :text then 'MyText'
164
+ when :boolean then false
165
+ when :references, :belongs_to then nil
166
+ else
167
+ ''
168
+ end
169
+ end
170
+
171
+ def plural_name
172
+ name.sub(/_id$/, '').pluralize
173
+ end
174
+
175
+ def singular_name
176
+ name.sub(/_id$/, '').singularize
177
+ end
178
+
179
+ def human_name
180
+ name.humanize
181
+ end
182
+
183
+ def index_name
184
+ @index_name ||= if polymorphic?
185
+ %w(id type).map { |t| "#{name}_#{t}" }
186
+ else
187
+ column_name
188
+ end
189
+ end
190
+
191
+ def column_name
192
+ @column_name ||= reference? ? "#{name}_id" : name
193
+ end
194
+
195
+ def foreign_key?
196
+ !!(name =~ /_id$/)
197
+ end
198
+
199
+ def reference?
200
+ self.class.reference?(type)
201
+ end
202
+
203
+ def polymorphic?
204
+ attr_options.key?(:polymorphic)
205
+ end
206
+
207
+ def has_index?
208
+ @has_index
209
+ end
210
+
211
+ def has_uniq_index?
212
+ @has_uniq_index
213
+ end
214
+
215
+ def password_digest?
216
+ name == 'password' && type == :digest
217
+ end
218
+
219
+ def inject_options
220
+ ''.tap { |s| @attr_options.each { |k, v| s << ", #{k}: #{v.inspect}" } }
221
+ end
222
+
223
+ def inject_index_options
224
+ has_uniq_index? ? ', unique: true' : ''
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,21 @@
1
+ require 'thor'
2
+ require 'active_support/core_ext/string'
3
+
4
+ module Gris
5
+ module Generators
6
+ class ScaffoldGenerator < Thor::Group
7
+ include Thor::Actions
8
+
9
+ source_root "#{File.dirname(__FILE__)}/templates/scaffold"
10
+
11
+ argument :app_name
12
+ argument :app_path, optional: true
13
+
14
+ def generate
15
+ say 'Generating scaffold...'
16
+ directory '.', (app_path || app_name)
17
+ say 'Done!', :green
18
+ end
19
+ end
20
+ end
21
+ end