hexx 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +45 -0
  3. data/CHANGELOG.rdoc +1 -0
  4. data/LICENSE.rdoc +21 -0
  5. data/README.rdoc +235 -0
  6. data/Rakefile +31 -0
  7. data/bin/hexx +54 -0
  8. data/lib/generators/base.rb +59 -0
  9. data/lib/generators/controller/controller.rb +87 -0
  10. data/lib/generators/controller/templates/controller.erb +18 -0
  11. data/lib/generators/controller/templates/controller_action.erb +8 -0
  12. data/lib/generators/controller/templates/controller_action_spec.erb +28 -0
  13. data/lib/generators/controller/templates/controller_spec.erb +52 -0
  14. data/lib/generators/controller/templates/routing_action_spec.erb +10 -0
  15. data/lib/generators/controller/templates/routing_spec.erb +10 -0
  16. data/lib/generators/dependency/dependency.rb +34 -0
  17. data/lib/generators/dependency/templates/dependency_setting.erb +4 -0
  18. data/lib/generators/dependency/templates/dependency_setting_spec.erb +34 -0
  19. data/lib/generators/dependency/templates/module_spec.erb +22 -0
  20. data/lib/generators/domain/domain.rb +24 -0
  21. data/lib/generators/domain/templates/spec.erb +84 -0
  22. data/lib/generators/install/install.rb +115 -0
  23. data/lib/generators/install/templates/CHANGELOG.erb +1 -0
  24. data/lib/generators/install/templates/Gemfile.erb +5 -0
  25. data/lib/generators/install/templates/LICENSE.erb +21 -0
  26. data/lib/generators/install/templates/README.erb +55 -0
  27. data/lib/generators/install/templates/Rakefile.erb +34 -0
  28. data/lib/generators/install/templates/bin/rails.erb +11 -0
  29. data/lib/generators/install/templates/config/routes.erb +6 -0
  30. data/lib/generators/install/templates/gemspec.erb +29 -0
  31. data/lib/generators/install/templates/lib/engine.erb +12 -0
  32. data/lib/generators/install/templates/lib/lib.erb +10 -0
  33. data/lib/generators/install/templates/lib/version.erb +4 -0
  34. data/lib/generators/install/templates/spec/coveralls.erb +4 -0
  35. data/lib/generators/install/templates/spec/database_cleaner.erb +27 -0
  36. data/lib/generators/install/templates/spec/factory_girl.erb +6 -0
  37. data/lib/generators/install/templates/spec/factory_girl_rails.erb +1 -0
  38. data/lib/generators/install/templates/spec/focus.erb +5 -0
  39. data/lib/generators/install/templates/spec/garbage_collection.erb +11 -0
  40. data/lib/generators/install/templates/spec/i18n.erb +1 -0
  41. data/lib/generators/install/templates/spec/migrations.erb +3 -0
  42. data/lib/generators/install/templates/spec/rails.erb +6 -0
  43. data/lib/generators/install/templates/spec/random_order.erb +4 -0
  44. data/lib/generators/install/templates/spec/rspec.erb +5 -0
  45. data/lib/generators/install/templates/spec/spec_helper.erb +12 -0
  46. data/lib/generators/install/templates/spec/timecop.erb +1 -0
  47. data/lib/generators/request/request.rb +52 -0
  48. data/lib/generators/request/templates/request_spec.erb +73 -0
  49. data/lib/generators/use_case/templates/use_case.erb +29 -0
  50. data/lib/generators/use_case/templates/use_case_spec.erb +77 -0
  51. data/lib/generators/use_case/use_case.rb +31 -0
  52. data/lib/hexx.rb +2 -0
  53. data/lib/hexx/exceptions/not_found_error.rb +12 -0
  54. data/lib/hexx/exceptions/record_invalid.rb +12 -0
  55. data/lib/hexx/exceptions/runtime_error.rb +24 -0
  56. data/lib/hexx/exceptions/use_case_invalid.rb +12 -0
  57. data/lib/hexx/models.rb +82 -0
  58. data/lib/hexx/settings.rb +47 -0
  59. data/lib/hexx/use_case.rb +228 -0
  60. data/lib/hexx/version.rb +4 -0
  61. data/spec/hexx/exceptions/not_found_error_spec.rb +27 -0
  62. data/spec/hexx/exceptions/record_invalid_spec.rb +27 -0
  63. data/spec/hexx/exceptions/runtime_error_spec.rb +61 -0
  64. data/spec/hexx/exceptions/use_case_invalid_spec.rb +27 -0
  65. data/spec/hexx/models_spec.rb +64 -0
  66. data/spec/hexx/settings_spec.rb +51 -0
  67. data/spec/hexx/use_case_spec.rb +262 -0
  68. data/spec/spec_helper.rb +3 -0
  69. data/spec/support/initializers/coveralls.rb +3 -0
  70. data/spec/support/initializers/focus.rb +5 -0
  71. data/spec/support/initializers/garbage_collection.rb +11 -0
  72. data/spec/support/initializers/i18n.rb +1 -0
  73. data/spec/support/initializers/random_order.rb +4 -0
  74. data/spec/support/initializers/rspec.rb +5 -0
  75. metadata +236 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f14eb96fccf59e4cff2b528b7af4022bc1a06ba0
4
+ data.tar.gz: 129481aec498d829b4cfefa8826342d3c2656f52
5
+ SHA512:
6
+ metadata.gz: 33cda7123f07260c0c60b11e81d57fa2acdd15df6e3a918e21956f61564fbd76de750c263d82cdef7b324f757b369ab318ecc5137cf0220faf63d11d465c1da0
7
+ data.tar.gz: 2f9fc12483c459cde61bb5581a74e3c126f92722d741d323160af374eeefda52bec304735721f7bee9ca8989fc7b6aebe14bc868964f41c5b8617324f46db203
@@ -0,0 +1,45 @@
1
+ Lint/HandleExceptions:
2
+ Exclude:
3
+ - 'spec/**/*'
4
+
5
+ Lint/RescueException:
6
+ Exclude:
7
+ - 'spec/**/*'
8
+
9
+ Style/AccessorMethodName:
10
+ Exclude:
11
+ - 'spec/**/*'
12
+
13
+ Style/ClassAndModuleChildren:
14
+ Exclude:
15
+ - 'spec/**/*'
16
+
17
+ Style/Documentation:
18
+ Exclude:
19
+ - 'spec/**/*'
20
+
21
+ Style/EmptyLinesAroundBody:
22
+ Enabled: false
23
+
24
+ Style/EmptyLineBetweenDefs:
25
+ Exclude:
26
+ - 'spec/**/*'
27
+
28
+ Style/RaiseArgs:
29
+ EnforcedStyle: compact
30
+
31
+ Style/SingleLineMethods:
32
+ Exclude:
33
+ - 'spec/**/*'
34
+
35
+ Style/SpecialGlobalVars:
36
+ Exclude:
37
+ - 'Gemfile'
38
+ - '*.gemspec'
39
+
40
+ Style/StringLiterals:
41
+ EnforcedStyle: double_quotes
42
+
43
+ Style/TrivialAccessors:
44
+ Exclude:
45
+ - 'spec/**/*'
@@ -0,0 +1 @@
1
+ = CHANGELOG
@@ -0,0 +1,21 @@
1
+ = The MIT License
2
+
3
+ Copyright (c) 2014 Andrew Kozin, https://github.com/nepalez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,235 @@
1
+ = Hexx
2
+
3
+ {<img src="http://img.shields.io/travis/nepalez/hexx.svg?style=flat" alt="Bild Status" />}[https://travis-ci.org/nepalez/hexx]
4
+ {<img src="http://img.shields.io/codeclimate/github/nepalez/hexx.svg?style=flat" alt="Code Metrics" />}[https://codeclimate.com/github/nepalez/hexx]
5
+ {<img src="http://img.shields.io/gemnasium/nepalez/hexx.svg?style=flat" alt="Dependency Status" />}[https://gemnasium.com/nepalez/hexx]
6
+ {<img src="http://img.shields.io/coveralls/nepalez/hexx.svg?style=flat" alt="Coverage Status" />}[https://coveralls.io/r/nepalez/hexx]
7
+ {<img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License" />}[https://github.com/nepalez/hexx/blob/master/LICENSE.rdoc]
8
+
9
+ A part of framework for rapid Rails API development following the hexagonal
10
+ architecture.
11
+
12
+ Provides:
13
+ * +UseCase+ base class for decoupling a domain business logics from a controller
14
+ * Thor generators for scaffolding use cases, controllers and routes.
15
+
16
+ == Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'hexx', git: "https://github.com/nepalez/hexx.git"
21
+
22
+ == Usage
23
+
24
+ Controllers are expected to serve as a thin delivery layer between a client
25
+ and a use case. They should only orchestrate interactions of use case with its
26
+ listeners: the controller itself, mailers and other outer services.
27
+
28
+ In a typical case the controller should constist from:
29
+
30
+ * actions serving as <b>outer ports</b> to client, listening to routes and
31
+ calling corresponding use cases
32
+ * methods servinge as <b>inner ports</b> called by use case notifications
33
+ (see wisper[https://github.com/krisleech/wisper.git] for more details).
34
+
35
+ === Use case
36
+
37
+ # app/my_project/use_cases/get_item.rb
38
+ require 'hexx'
39
+
40
+ module MyProject
41
+ class GetItem < Hexx::Base
42
+
43
+ # whitelists parameters taken by an initializer
44
+ allow_params :id
45
+
46
+ # runs some validation (on the basis of ActiveModel::Validations)
47
+ validate :id, presence: true
48
+
49
+ # runs a use case
50
+ def run!
51
+ validate!
52
+ # provide case-specific business rules
53
+ end
54
+ end
55
+ end
56
+
57
+ === Rails controller
58
+
59
+ # app/controllers/my_project/rails/api/v1/items_controller.rb
60
+ require 'my_project'
61
+ require 'rails'
62
+
63
+ module MyProject
64
+ module Rails
65
+ module Api
66
+ module V1
67
+ class ItemsController < ActionController::Base
68
+
69
+ # An action (outer port) of the controller
70
+ def show
71
+ use_case = GetItem.new params.allow(:id)
72
+ # subscribes the controller for listening for the use case
73
+ # notifications via the controller's inner ports.
74
+ use_case.subscribe self
75
+ use_case.run
76
+ end
77
+
78
+ # Inner port to be called by a use case
79
+ def found(item)
80
+ render json: { success: true, item: item }, status: 200
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ <i>Note the module above is not +my_project+, but +my_project-rails+
88
+ (a delivery mechanism for +my_project+).</i>
89
+
90
+ === Models and Entities
91
+
92
+ The module also defines module <tt>Hexx::Models</tt> for including to
93
+ entities or active_record models. It defines a +validate!+ method:
94
+
95
+ class User < ActiveRecord::Base
96
+ include Hexx::Models
97
+ end
98
+
99
+ User.new.validate!
100
+ # => raises Hexx::RecordInvalid error if the record isn't valid.
101
+
102
+ It also redefines +attr_accessor+, +attr_reader+ and +attr_writer+ helpers
103
+ to allow attribute coercion.
104
+
105
+ require "attributes/string"
106
+
107
+ class User < ActiveRecord::Base
108
+ include Hexx::Models
109
+
110
+ attr_accessor :name, type: Attributes::String
111
+ end
112
+
113
+ This coerces the name with an Attributes::String class object as if its
114
+ setter and getter were:
115
+
116
+ def name=(value)
117
+ super Attributes::String.new(value)
118
+ end
119
+
120
+ def name
121
+ Attributes::String.new(super)
122
+ end
123
+
124
+ == Generators
125
+
126
+ The module contains generators, written on the basis of the Thor, namely:
127
+
128
+ * request specs (acceptance tests);
129
+ * controllers with their specs (unit tests)
130
+ * routing specs (unit tests);
131
+ * domain specs (acceptance tests for use cases and models);
132
+ * use_cases with their specs (unit tests);
133
+
134
+ The scaffolded specs contains examples for writing tests rapidly following
135
+ some conventions.
136
+
137
+ === Request spec (acceptance test for the API)
138
+
139
+ Request specs used to test API from a user view - by sending requests
140
+ and checkign their results. Their should be written on the basis of
141
+ API blueprint. Their shouldn't check the internal states of application, such
142
+ as models, use_cases etc.
143
+
144
+ Task is called with 1 argument, that consists from two parts divided by colon:
145
+
146
+ * request type (get, post etc.)
147
+ * requests relative address
148
+
149
+ Example:
150
+
151
+ $ hexx request get:items/{id}
152
+ # => spec/requests/my_module/api/v1/get_items_id_spec.rb
153
+
154
+ API namespace (those /my_module/api/v1) will be added by default.
155
+
156
+ === Controller action with a corresponding route
157
+
158
+ A scaffolder provides tests for both a controller and its router. It
159
+ also scaffolds a controller action itself. The router action should be
160
+ added manually.
161
+
162
+ Except for the requests specs (above) the controller spec is an unit test
163
+ that checks outer and inner controller action in isolation from both
164
+ router and a use case. It only checks that outer actions calls proper use cases,
165
+ and inner actions returns proper responce to a user.
166
+
167
+ Task is called with 3 argumets:
168
+
169
+ * name of the controller
170
+ * request type and action name, divided by colon
171
+ * name of the use case for the action
172
+
173
+ Example:
174
+
175
+ $ hexx controller items get:show get_item
176
+ # => spec/routing/my_module/api/v1/items_routing_spec.rb
177
+ # => spec/controllers/my_module/api/v1/items_controller_spec.rb
178
+ # => app/controllers/my_module/api/v1/items_controller.rb
179
+
180
+ ==== Acceptance spec for the domain
181
+
182
+ The domain spec checks the inner part of the domain (use_cases and models) as
183
+ a whole. Because the domain interacts with outer word through the use cases
184
+ only, the acceptance test should call use_cases and check its results.
185
+ It should not check inner states of the domain (models, entities, repositories,
186
+ databases etc.)
187
+
188
+ Task is called with 1 argument:
189
+
190
+ * name of the use_case.
191
+
192
+ Example:
193
+
194
+ $ hexx domain get_item
195
+ # => spec/my_module/domain/get_item_spec.rb
196
+
197
+ ==== UseCase
198
+
199
+ The unit tests for checking a use_case in isolation.
200
+
201
+ Task is called with 1 argument:
202
+
203
+ * name of the use_case.
204
+
205
+ Example:
206
+
207
+ $ hexx use_case get_item
208
+ # => spec/my_module/use_cases/get_item_spec.rb
209
+ # => app/my_module/use_cases/get_item.rb
210
+
211
+ ==== Dependency
212
+
213
+ Scaffolds the external dependency setting with a corresponding specs.
214
+
215
+ Task is called with 1 argument:
216
+
217
+ * name of the dependency.
218
+
219
+ Example:
220
+
221
+ $hexx dependency some_module
222
+ # => spec/my_module_spec.rb
223
+ # => lib/my_module.rb
224
+
225
+ == Relevant Links
226
+
227
+ 1:: Matt Wynne's talk {Hexagonal Rails}[http://www.confreaks.com/videos/977-goruco2012-hexagonal-rails]
228
+
229
+ == Changelog
230
+
231
+ To review changes between versions see the {CHANGELOG}[CHANGELOG.rdoc].
232
+
233
+ == License
234
+
235
+ The project is distributed under the {MIT LICENSE}[LICENSE.rdoc].
@@ -0,0 +1,31 @@
1
+ begin
2
+ require "bundler/setup"
3
+ rescue LoadError
4
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
+ end
6
+
7
+ begin
8
+ require "rdoc/task"
9
+ rescue LoadError
10
+ require "rdoc/rdoc"
11
+ require "rake/rdoctask"
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = "rdoc"
17
+ rdoc.title = "Hexx"
18
+ rdoc.options << "--line-numbers"
19
+ rdoc.rdoc_files.include("README.rdoc")
20
+ rdoc.rdoc_files.include("lib/**/*.rb")
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
25
+ require "bundler/gem_tasks"
26
+ require "rspec/core/rake_task"
27
+
28
+ RSpec::Core::RakeTask.new(:spec)
29
+ task :default do
30
+ sh "bundle exec rspec spec"
31
+ end
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ require "thor"
3
+ require "rubygems"
4
+
5
+ root = File.dirname File.dirname(__FILE__)
6
+ Dir[File.join(root, "lib/generators/**/*.rb")].each { |file| require file }
7
+
8
+ # Scaffold tasks for the Hexx based projects.
9
+ class HexxRunner < Thor
10
+
11
+ desc "install", "scaffolds a new project"
12
+ def install
13
+ script = Hexx::Generators::Install.new [project]
14
+ script.invoke_all
15
+ end
16
+
17
+ desc "request", "scaffolds a new request spec (acceptance test)"
18
+ def request(name)
19
+ script = Hexx::Generators::Request.new [project, name]
20
+ script.invoke_all
21
+ end
22
+
23
+ desc "controller", "scaffolds a new route with a controller action"
24
+ def controller(name, route, usecase)
25
+ script = Hexx::Generators::Controller.new [project, name, route, usecase]
26
+ script.invoke_all
27
+ end
28
+
29
+ desc "domain", "scaffolds a new acceptance test for the domain"
30
+ def domain(name)
31
+ script = Hexx::Generators::Domain.new [project, name]
32
+ script.invoke_all
33
+ end
34
+
35
+ desc "use_case", "scaffolds a new use case"
36
+ def use_case(name)
37
+ script = Hexx::Generators::UseCase.new [project, name]
38
+ script.invoke_all
39
+ end
40
+
41
+ desc "depencency", "scaffolds a dependency"
42
+ def dependency(name)
43
+ script = Hexx::Generators::Dependency.new [project, name]
44
+ script.invoke_all
45
+ end
46
+
47
+ private
48
+
49
+ def project
50
+ Dir.getwd.split("/").last.to_s.downcase
51
+ end
52
+ end
53
+
54
+ HexxRunner.start
@@ -0,0 +1,59 @@
1
+ require "thor"
2
+ require "extlib"
3
+
4
+ module Hexx
5
+ module Generators
6
+
7
+ # Base class for scaffolders.
8
+ class Base < Thor::Group
9
+ include Thor::Actions
10
+
11
+ argument :project
12
+ argument :name
13
+
14
+ def self.source_root(file = __FILE__)
15
+ root = File.dirname File.expand_path(file)
16
+ @source_root ||= File.join(root, "templates")
17
+ end
18
+
19
+ private
20
+
21
+ def gem_name
22
+ @gem_name ||= project.snake_case
23
+ end
24
+
25
+ def module_name
26
+ @module_name ||= project.camel_case
27
+ end
28
+
29
+ def class_name
30
+ @class_name ||= name.camel_case
31
+ end
32
+
33
+ def file_name
34
+ @file_name ||= name.snake_case
35
+ end
36
+
37
+ def action_type(string)
38
+ return @action_type if @action_type
39
+ list = string.split(":")
40
+ list.count > 1 ? list.first.downcase : "get"
41
+ end
42
+
43
+ def action_name(string)
44
+ string.split(":").last.downcase
45
+ end
46
+
47
+ def inject_template_into_file(source, file, options)
48
+ source = File.expand_path(find_in_source_paths(source.to_s))
49
+ context = instance_eval("binding")
50
+ content = ERB.new(::File.binread(source), nil, "-", "@output_buffer")
51
+ .result(context)
52
+
53
+ inject_into_file file, options do
54
+ content
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end