hexx 0.0.1

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