appfuel 0.2.0

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