appfuel 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +25 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +1156 -0
  6. data/.travis.yml +19 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +9 -0
  9. data/README.md +38 -0
  10. data/Rakefile +6 -0
  11. data/appfuel.gemspec +42 -0
  12. data/bin/console +7 -0
  13. data/bin/setup +8 -0
  14. data/lib/appfuel.rb +210 -0
  15. data/lib/appfuel/application.rb +4 -0
  16. data/lib/appfuel/application/app_container.rb +223 -0
  17. data/lib/appfuel/application/container_class_registration.rb +22 -0
  18. data/lib/appfuel/application/container_key.rb +201 -0
  19. data/lib/appfuel/application/qualify_container_key.rb +76 -0
  20. data/lib/appfuel/application/root.rb +140 -0
  21. data/lib/appfuel/cli_msg_request.rb +19 -0
  22. data/lib/appfuel/configuration.rb +14 -0
  23. data/lib/appfuel/configuration/definition_dsl.rb +175 -0
  24. data/lib/appfuel/configuration/file_loader.rb +61 -0
  25. data/lib/appfuel/configuration/populate.rb +95 -0
  26. data/lib/appfuel/configuration/search.rb +45 -0
  27. data/lib/appfuel/db_model.rb +16 -0
  28. data/lib/appfuel/domain.rb +7 -0
  29. data/lib/appfuel/domain/criteria.rb +436 -0
  30. data/lib/appfuel/domain/domain_name_parser.rb +44 -0
  31. data/lib/appfuel/domain/dsl.rb +247 -0
  32. data/lib/appfuel/domain/entity.rb +242 -0
  33. data/lib/appfuel/domain/entity_collection.rb +87 -0
  34. data/lib/appfuel/domain/expr.rb +127 -0
  35. data/lib/appfuel/domain/value_object.rb +7 -0
  36. data/lib/appfuel/errors.rb +104 -0
  37. data/lib/appfuel/feature.rb +2 -0
  38. data/lib/appfuel/feature/action_loader.rb +25 -0
  39. data/lib/appfuel/feature/initializer.rb +43 -0
  40. data/lib/appfuel/handler.rb +6 -0
  41. data/lib/appfuel/handler/action.rb +17 -0
  42. data/lib/appfuel/handler/base.rb +103 -0
  43. data/lib/appfuel/handler/command.rb +18 -0
  44. data/lib/appfuel/handler/inject_dsl.rb +88 -0
  45. data/lib/appfuel/handler/validator_dsl.rb +256 -0
  46. data/lib/appfuel/initialize.rb +70 -0
  47. data/lib/appfuel/initialize/initializer.rb +68 -0
  48. data/lib/appfuel/msg_request.rb +207 -0
  49. data/lib/appfuel/predicates.rb +10 -0
  50. data/lib/appfuel/presenter.rb +18 -0
  51. data/lib/appfuel/presenter/base.rb +7 -0
  52. data/lib/appfuel/repository.rb +73 -0
  53. data/lib/appfuel/repository/base.rb +72 -0
  54. data/lib/appfuel/repository/initializer.rb +19 -0
  55. data/lib/appfuel/repository/mapper.rb +203 -0
  56. data/lib/appfuel/repository/mapping_dsl.rb +210 -0
  57. data/lib/appfuel/repository/mapping_entry.rb +76 -0
  58. data/lib/appfuel/repository/mapping_registry.rb +121 -0
  59. data/lib/appfuel/repository_runner.rb +60 -0
  60. data/lib/appfuel/request.rb +53 -0
  61. data/lib/appfuel/response.rb +96 -0
  62. data/lib/appfuel/response_handler.rb +79 -0
  63. data/lib/appfuel/root_module.rb +31 -0
  64. data/lib/appfuel/run_error.rb +9 -0
  65. data/lib/appfuel/storage.rb +3 -0
  66. data/lib/appfuel/storage/db.rb +4 -0
  67. data/lib/appfuel/storage/db/active_record_model.rb +42 -0
  68. data/lib/appfuel/storage/db/mapper.rb +213 -0
  69. data/lib/appfuel/storage/db/migration_initializer.rb +42 -0
  70. data/lib/appfuel/storage/db/migration_runner.rb +15 -0
  71. data/lib/appfuel/storage/db/migration_tasks.rb +18 -0
  72. data/lib/appfuel/storage/db/repository.rb +231 -0
  73. data/lib/appfuel/storage/db/repository_query.rb +13 -0
  74. data/lib/appfuel/storage/file.rb +1 -0
  75. data/lib/appfuel/storage/file/base.rb +32 -0
  76. data/lib/appfuel/storage/memory.rb +2 -0
  77. data/lib/appfuel/storage/memory/mapper.rb +30 -0
  78. data/lib/appfuel/storage/memory/repository.rb +37 -0
  79. data/lib/appfuel/types.rb +53 -0
  80. data/lib/appfuel/validation.rb +80 -0
  81. data/lib/appfuel/validation/validator.rb +59 -0
  82. data/lib/appfuel/validation/validator_pipe.rb +47 -0
  83. data/lib/appfuel/version.rb +3 -0
  84. metadata +335 -0
@@ -0,0 +1,42 @@
1
+ require 'rake'
2
+
3
+ module Appfuel
4
+ module Db
5
+ module MigrationsInitializer
6
+
7
+ def self.call(settings = {})
8
+ root_name = settings[:root_name] || Appfuel.default_app_name
9
+ container = Appfuel.app_container(root_name)
10
+
11
+ config = container[:config]
12
+ root_path = container[:root_path]
13
+ env = container[:env]
14
+ db_path = config[:db][:path]
15
+ migrations_paths = config[:db][:migrations_path]
16
+ db_config = config[:db][:main]
17
+
18
+ db_tasks = settings.fetch(:db_tasks) {
19
+ ActiveRecord::Tasks::DatabaseTasks
20
+ }
21
+
22
+ db_migrator = settings.fetch(:db_migrator) {
23
+ ActiveRecord::Migrator
24
+ }
25
+
26
+ active_record_base = settings.fetch(:active_record_base) {
27
+ ActiveRecord::Base
28
+ }
29
+
30
+ active_record_base.configurations = {env => db_config}
31
+
32
+ db_tasks.root = root_path
33
+ db_tasks.env = env
34
+ db_tasks.db_dir = db_path
35
+ db_tasks.migrations_paths = migrations_paths
36
+ db_tasks.database_configuration = db_config
37
+
38
+ db_migrator.migrations_paths = migrations_paths
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ require 'rake'
2
+ require_relative 'migration_initializer'
3
+ require_relative 'migration_tasks'
4
+
5
+ module Appfuel
6
+ module Db
7
+ module MigrationRunner
8
+ def self.call(cmd, data = {})
9
+ tasks = MigrationTasks.new
10
+ tasks.install_tasks
11
+ Rake::Task[cmd].invoke
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ require 'rake'
2
+ module Appfuel
3
+ module Db
4
+ class MigrationTasks
5
+ include Rake::DSL
6
+
7
+ def install_tasks
8
+ load "active_record/railties/databases.rake"
9
+
10
+ namespace :db do
11
+ task :environment do
12
+ MigrationsInitializer.call
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,231 @@
1
+ module Appfuel
2
+ module Db
3
+ class Repository < Appfuel::Repository::Base
4
+ class << self
5
+ def create_mapper(maps = nil)
6
+ Mapper.new(maps)
7
+ end
8
+ end
9
+
10
+ attr_reader :response_handler
11
+
12
+ def create(entity, exclude = [])
13
+ data = to_storage(entity, exclude: ['id'])
14
+ db_results = []
15
+ data.each do |db_class_key, mapped|
16
+ db_model = db_class(db_class_key)
17
+ db_results << db_model.create(mapped)
18
+ end
19
+
20
+ build(name: entity.domain_name, storage: db_results, type: :db)
21
+ end
22
+
23
+ def db_class(key)
24
+ app_container[key]
25
+ end
26
+
27
+ # Used when the automated query methods don't suite your use case. It
28
+ # is assumed that the method executed will honor the same interfaces as
29
+ # query does
30
+ #
31
+ # @param criteria [SpCore::Criteria]
32
+ # @return [Dataset]
33
+ def execute_criteria(criteria)
34
+ query_method = "#{criteria.exec}_manual_query"
35
+ validate_query_method(query_method)
36
+
37
+ public_send(query_method, criteria)
38
+ end
39
+
40
+ # Use the criteria entity's basename as a convention to find a method
41
+ # on the repository that returns the necessary relation (db scope) for
42
+ # which to add conditions that will be used to complete the query.
43
+ #
44
+ #
45
+ # @param criteria [SpCore::Criteria]
46
+ # @return [ActiveRecord::Relation]
47
+ def query_relation(criteria)
48
+ query_method = "#{criteria.domain}_query"
49
+ validate_query_method(query_method)
50
+
51
+ public_send(query_method)
52
+ end
53
+
54
+ # Handles the treatment of the relation when the recordset is empty
55
+ # based on the criteria.
56
+ #
57
+ # @param criteria [SpCore::Criteria]
58
+ # @return [SpCore::Error, SpCore::Domain::EntityNotFound, nil]
59
+ def handle_empty_relation(criteria, relation)
60
+ return nil unless relation.blank?
61
+
62
+ if criteria.error_on_empty_dataset?
63
+ return error(criteria.domain => ["#{criteria.domain} not found"])
64
+ end
65
+
66
+ if criteria.single?
67
+ return create_entity_not_found(criteria)
68
+ end
69
+ end
70
+
71
+ # Null object used when you can not find a given entity
72
+ #
73
+ # @param criteria [SpCore::Criteria]
74
+ # @return SpCore::Domain::EntityNotFound
75
+ def create_entity_not_found(criteria)
76
+ Appfuel::Domain::EntityNotFound.new(entity_name: criteria.domain_name)
77
+ end
78
+
79
+ # Apply where, order and limit clauses to the relation based on the
80
+ # criteria.
81
+ #
82
+ # @param criteria [SpCore::Criteria]
83
+ # @param relation [mixed]
84
+ # @return relation
85
+ def apply_query_conditions(criteria, relation)
86
+ relation = where(criteria, relation)
87
+ relation = order(criteria, relation)
88
+ relation = limit(criteria, relation)
89
+ relation
90
+ end
91
+
92
+ # We have an interface for getting all recordsets separately because
93
+ # this is usually done with no filters or limits.
94
+ #
95
+ # @param criteria [SpCore::Criteria]
96
+ # @param relation
97
+ # @return relation
98
+ def apply_query_all(criteria, relation)
99
+ unless criteria.all?
100
+ fail "This interface can only be used when the criteria :all is used"
101
+ end
102
+
103
+ order(criteria, relation.all)
104
+ end
105
+
106
+ # Determines which query conditions to apply to the relation
107
+ #
108
+ # @param criteria [SpCore::Criteria]
109
+ # @param relation
110
+ # @return relation
111
+ def handle_query_conditions(criteria, relation)
112
+ if criteria.all?
113
+ return apply_query_all(criteria, relation)
114
+ end
115
+
116
+ apply_query_conditions(criteria, relation)
117
+ end
118
+
119
+ # Factory method to create a pagination result
120
+ #
121
+ # @param data [Hash]
122
+ # @return [SpCore::Pagination::Result]
123
+ def create_pager_result(data)
124
+ Appfuel::Pagination::Result.new(data)
125
+ end
126
+
127
+ # Factory method to create a domain entity
128
+ #
129
+ # @param domain_name [String]
130
+ # @return [SpCore::Domain::EntityCollection]
131
+ def create_entity_collection(domain_name)
132
+ Appfuel::Domain::EntityCollection.new(domain_name)
133
+ end
134
+
135
+ # Creates a lambda to used with the entity collection
136
+ #
137
+ # @param criteria [SpCore::Criteria]
138
+ # @param relation [Object]
139
+ # @param builder [Object]
140
+ # @return lambda
141
+ def entity_loader(criteria, relation, builder)
142
+ -> { load_collection(criteria, relation, builder) }
143
+ end
144
+
145
+ # A collection is usually loaded within an entity collection via
146
+ # a lambda. It setups up pagination results and builds an entity
147
+ # foreach record in the list
148
+ #
149
+ # @param criteria [SpCore::Criteria]
150
+ # @param relation [Object]
151
+ # @param builder [Object]
152
+ # @return [Hash]
153
+ def load_collection(criteria, relation, builder)
154
+ data = { items: [] }
155
+ unless criteria.disable_pagination?
156
+ relation = relation.page(criteria.page).per(criteria.per_page)
157
+ data[:pager] = create_pager_result(
158
+ total_pages: relation.total_pages,
159
+ current_page: relation.current_page,
160
+ total_count: relation.total_count,
161
+ page_limit: relation.limit_value,
162
+ page_size: relation.size
163
+ )
164
+ end
165
+
166
+ relation.each do |db_item|
167
+ data[:items] << builder.call(db_item)
168
+ end
169
+ data
170
+ end
171
+
172
+ # Create an entity collection and assign the entity loader with
173
+ # the entity builder class.
174
+ #
175
+ # @param criteria [SpCore::Criteria]
176
+ # @param relation relation
177
+ # @return SpCore::Domain::EntityCollection
178
+ def build_criteria_entities(criteria, relation)
179
+ builder = create_entity_builder(criteria.domain_name)
180
+ result = handle_empty_relation(criteria, relation)
181
+ # when this has a result it means an empty relation has been
182
+ # handled and ready for return otherwise it was a no op
183
+ return result if result
184
+
185
+ if criteria.single?
186
+ relation_method = criteria.first? ? :first : :last
187
+ return builder.call(relation.send(relation_method))
188
+ end
189
+
190
+ collection = create_entity_collection(criteria.domain_name)
191
+ collection.entity_loader = entity_loader(criteria, relation, builder)
192
+ collection
193
+ end
194
+
195
+ # Query will use the database model to build a query based on the
196
+ # given criteria. It supports where, order and limit conditions.
197
+ #
198
+ # @param criteria [SpCore::Criteria]
199
+ # @return [SpCore::Domain::Entity, SpCore::Domain::EntityCollection]
200
+ def query(criteria)
201
+ return execute_criteria(criteria) if criteria.exec?
202
+
203
+ begin
204
+ relation = query_relation(criteria)
205
+ relation = handle_query_conditions(criteria, relation)
206
+ build_criteria_entities(criteria, relation)
207
+ rescue => e
208
+ msg = "query failed for #{criteria.domain}: #{e.class} #{e.message}"
209
+ err = RuntimeError.new(msg)
210
+ err.set_backtrace(e.backtrace)
211
+ raise err
212
+ end
213
+ end
214
+
215
+ private
216
+
217
+ def raise_error(err, message)
218
+ error = RuntimeError.new(message)
219
+ error.set_backtrace(err.backtrace)
220
+ raise error
221
+ end
222
+
223
+ def validate_entity_id(entity)
224
+ if entity.id == Types::Undefined
225
+ fail("entity id is #{entity.id}")
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+
@@ -0,0 +1,13 @@
1
+ module Appfuel
2
+ module Db
3
+ module RepositoryQuery
4
+
5
+
6
+ private
7
+
8
+ def validate_query_method(method)
9
+ fail "Could not execute method #{method}" unless respond_to?(method)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ require_relative 'file/base'
@@ -0,0 +1,32 @@
1
+ module Appfuel
2
+ module File
3
+ class Base
4
+ attr_reader :filepath
5
+
6
+ def initialize(name, path)
7
+
8
+ end
9
+
10
+ def attributes
11
+
12
+ end
13
+
14
+ def exsits?(data)
15
+
16
+ end
17
+
18
+ def where(data)
19
+
20
+ end
21
+
22
+ def limit(nbr)
23
+
24
+ end
25
+
26
+ def order(data)
27
+
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'memory/mapper'
2
+ require_relative 'memory/repository'
@@ -0,0 +1,30 @@
1
+ module Appfuel
2
+ module Memory
3
+ class Mapper < Appfuel::Repository::Mapper
4
+
5
+ def to_storage(domain, opts = {})
6
+ excluded = opts[:exclude] || []
7
+
8
+ data = {}
9
+ each_entity_attr(domain.domain_name) do |entry|
10
+ attr_name = entry.storage_attr
11
+ next if excluded.include?(attr_name) || entry.skip?
12
+
13
+ data[attr_name] = entity_value(domain, entry)
14
+ end
15
+ data
16
+ end
17
+
18
+
19
+ def exists?(criteria)
20
+ domain_expr = criteria.exists_expr
21
+ domain_name = domain_expr.domain_name
22
+ domain_attr = domain_expr.domain_attr
23
+
24
+ db_expr = create_db_expr(domain_name, domain_attr)
25
+ db_model = db_class_mapped(domain_name, domain_attr)
26
+ db_model.exists?([db_expr.string, db_expr.values])
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ module Appfuel
2
+ module Memory
3
+ class Repository < Appfuel::Repository::Base
4
+
5
+ class << self
6
+ def create_mapper(maps = nil)
7
+ Mapper.new(maps)
8
+ end
9
+ end
10
+
11
+ attr_reader :items, :sequence
12
+ def initialize
13
+ @items = {}
14
+ @sequence = 0
15
+ end
16
+
17
+
18
+ def create(entity)
19
+ id = sequence_id
20
+ entity.id = id
21
+ data = to_storage(entity)
22
+ items[id] = data
23
+
24
+
25
+ build(name: entity.domain_name, storage: data)
26
+ end
27
+
28
+ def build(name:, storage:, **inputs)
29
+ super(type: :memory, name: name, storage: storage, **inputs)
30
+ end
31
+
32
+ def sequence_id
33
+ @sequence + 1
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ require 'dry/core/constants'
2
+ require 'dry-equalizer'
3
+ require 'dry-container'
4
+ require 'dry-types'
5
+
6
+ Dry::Types.load_extensions(:maybe)
7
+
8
+ module Types
9
+ include Dry::Types.module
10
+ include Dry::Core::Constants
11
+
12
+ class << self
13
+
14
+ # @param key [String, Symbol]
15
+ # @return [Class]
16
+ def [](key)
17
+ Dry::Types[key]
18
+ end
19
+
20
+ # @param key [String, Symbol]
21
+ # @return [Class]
22
+ def key?(key)
23
+ Dry::Types.container.key?(key)
24
+ end
25
+
26
+ # Container used to store validation types and domain entities
27
+ #
28
+ # @return Dry::Container
29
+ def container
30
+ Dry::Types.container
31
+ end
32
+
33
+ # Register a dependency that can be used for injection
34
+ #
35
+ # @param key [String, Symbol]
36
+ # @param klass [Class]
37
+ # @return
38
+ def register(key, klass)
39
+ container.register(key, klass)
40
+ end
41
+
42
+ def register_domain(klass, opts = {})
43
+ unless klass.respond_to?(:domain_name)
44
+ fail "Domain must be a Appfuel::Entity or respond to :domain_name"
45
+ end
46
+ name = opts.key?(:as) ? opt[:as] : klass.domain_name
47
+ p name
48
+ return if key?(name) && self[name] == klass
49
+
50
+ register(name, klass)
51
+ end
52
+ end
53
+ end