gris 0.0.2

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