appfuel 0.2.5 → 0.2.6

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/appfuel.gemspec +1 -1
  4. data/lib/appfuel/application/app_container.rb +0 -1
  5. data/lib/appfuel/application/dispatcher.rb +32 -0
  6. data/lib/appfuel/application/root.rb +4 -4
  7. data/lib/appfuel/application.rb +2 -1
  8. data/lib/appfuel/domain/domain_name_parser.rb +9 -5
  9. data/lib/appfuel/domain.rb +0 -7
  10. data/lib/appfuel/handler/base.rb +8 -14
  11. data/lib/appfuel/storage/db/mapper.rb +31 -35
  12. data/lib/appfuel/storage/db/repository.rb +53 -121
  13. data/lib/appfuel/storage/repository/base.rb +246 -0
  14. data/lib/appfuel/storage/repository/criteria.rb +317 -0
  15. data/lib/appfuel/{domain → storage/repository}/expr.rb +1 -1
  16. data/lib/appfuel/storage/repository/expr_conjunction.rb +41 -0
  17. data/lib/appfuel/{domain → storage/repository}/expr_parser.rb +10 -22
  18. data/lib/appfuel/{domain → storage/repository}/expr_transform.rb +7 -7
  19. data/lib/appfuel/{repository → storage/repository}/mapper.rb +8 -3
  20. data/lib/appfuel/storage/repository/order_expr.rb +51 -0
  21. data/lib/appfuel/storage/repository/runner.rb +62 -0
  22. data/lib/appfuel/storage/repository/search_parser.rb +50 -0
  23. data/lib/appfuel/storage/repository/search_transform.rb +58 -0
  24. data/lib/appfuel/{domain/criteria_settings.rb → storage/repository/settings.rb} +20 -5
  25. data/lib/appfuel/{repository.rb → storage/repository.rb} +13 -0
  26. data/lib/appfuel/storage.rb +1 -0
  27. data/lib/appfuel/version.rb +1 -1
  28. data/lib/appfuel.rb +0 -2
  29. metadata +21 -19
  30. data/lib/appfuel/domain/base_criteria.rb +0 -171
  31. data/lib/appfuel/domain/exists_criteria.rb +0 -57
  32. data/lib/appfuel/domain/expr_conjunction.rb +0 -27
  33. data/lib/appfuel/domain/search_criteria.rb +0 -137
  34. data/lib/appfuel/repository/base.rb +0 -86
  35. data/lib/appfuel/repository_runner.rb +0 -60
  36. /data/lib/appfuel/{repository → storage/repository}/initializer.rb +0 -0
  37. /data/lib/appfuel/{repository → storage/repository}/mapping_dsl.rb +0 -0
  38. /data/lib/appfuel/{repository → storage/repository}/mapping_entry.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab35093c6d5bbbed105926ad6cad55cde7616d5e
4
- data.tar.gz: e9bf722af35d2bad3dea89fba52eea61a02f3a00
3
+ metadata.gz: 730ab86942bed445ca95032499fb709e294614e5
4
+ data.tar.gz: 9b6fb2eeb4ad17c1737d9ff66a459edb11e86625
5
5
  SHA512:
6
- metadata.gz: b1bb7c55c07770ca5b77be1d2e2db73ed5d4542ac0209c0dd069efc9a5646f1d22c813964e07999e7d028c1b02096d53a9638dda6b6381a216618482128b0e95
7
- data.tar.gz: ff0140867e8db857de3377e39032f9e534fcdc2a7dc336e507cbe62e41da6f93cdf5c8029bc48ecc147d8b5b40f6f6eb779748b278a656af84f2b6f146a3e2f4
6
+ metadata.gz: 94cd230b06bd3fafb420f909c329f531b9e227d0ef9213432399fa531d05ac01914bcee6e026030733fb7c6ec5362047cf585b1cf5d139179092c98ad54f3cfe
7
+ data.tar.gz: 85e629ee6586d4495b700b4713fbc1a7af980af062e4394b67ccbd0b44cba48f4a1c0dd0f6f5da4ebe6c9a8aa810efb1c77c2f6251a3dba3b749001a1d634741
data/CHANGELOG.md CHANGED
@@ -21,3 +21,11 @@ All notable changes to this project will be documented in this file. (Pending ap
21
21
 
22
22
  ### Added
23
23
  - added exists interface to db mapper, will finalize the repo on next release
24
+
25
+ ## [[0.2.6]](https://github.com/rsb/appfuel/releases/tag/0.2.6) 2017-06-6
26
+
27
+ ### Added
28
+ - dispatcher mixin
29
+
30
+ ### Changed
31
+ - moved domain parsing to repository namespace
data/appfuel.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  # we have to lock dry-types due to failures I am encountering
24
24
  # when dynamically creating form validators. Not sure if it is the library
25
25
  # or the way I am using it.
26
- spec.add_dependency "activerecord", "~> 5.0.2"
26
+ spec.add_dependency "activerecord", "~> 5.1.0"
27
27
  spec.add_dependency "dry-types", "0.9.2"
28
28
  spec.add_dependency "dry-container", "~> 0.6"
29
29
  spec.add_dependency "dry-validation", "~> 0.10.5"
@@ -120,7 +120,6 @@ module Appfuel
120
120
  # @return nil
121
121
  def container_class_key(value = nil)
122
122
  return @container_class_key if value.nil?
123
- p "[container-class-key] #{self} => #{value}"
124
123
  @container_class_key = value
125
124
  end
126
125
 
@@ -0,0 +1,32 @@
1
+ module Appfuel
2
+ module Application
3
+ module Dispatcher
4
+
5
+ def dispatch(request, container)
6
+ begin
7
+ container[:feature_initializer].call(request.feature, container)
8
+ action = container[:action_loader].call(request.namespace, container)
9
+ response = action.run(inputs)
10
+ rescue => e
11
+ handler_error(e, container)
12
+ end
13
+
14
+ if response.failure?
15
+ handle_error(contaier, :failed, error)
16
+ end
17
+ end
18
+
19
+ private
20
+ def handle_error(e, container)
21
+ unless container.key?(:error_handler)
22
+ return default_error_handling(e, contianer) unless container.key?(:error_handler)
23
+ end
24
+
25
+ end
26
+
27
+ def default_error_handling(e, container)
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,6 +1,8 @@
1
1
  module Appfuel
2
2
  module Application
3
3
  module Root
4
+ extend Dispatcher
5
+
4
6
  # Initialize Appfuel by creating an application container for the
5
7
  # app represented by the root module passed in. The app container is
6
8
  # a dependency injection container that is used thought the app.
@@ -111,6 +113,7 @@ module Appfuel
111
113
  container.register(:root_path, root_path)
112
114
  container.register(:auto_register_classes, [])
113
115
  container.register(:repository_mappings, {})
116
+ container.register(:repository_cache, {})
114
117
  container.register(:repository_initializer, repo_initializer)
115
118
  container.register(:features_path, "#{root_name}/features")
116
119
  container.register(:feature_initializer, feature_initializer)
@@ -131,10 +134,7 @@ module Appfuel
131
134
  def call(route, inputs = {})
132
135
  container = Appfuel.app_container
133
136
  request = Request.new(route, inputs)
134
-
135
- container[:feature_initializer].call(request.feature, container)
136
- action = container[:action_loader].call(request.namespace, container)
137
- action.run(inputs)
137
+ dispatch(request, container)
138
138
  end
139
139
  end
140
140
  end
@@ -1,3 +1,4 @@
1
- require_relative 'application/root'
1
+ require_relative 'application/dispatcher'
2
2
  require_relative 'application/app_container'
3
+ require_relative 'application/root'
3
4
  require_relative 'application/container_class_registration'
@@ -5,9 +5,11 @@ module Appfuel
5
5
 
6
6
  # This parse the domain name string or object with domain_name method
7
7
  # and returns an array with feature, domain and name.
8
+ #
8
9
  # @example
9
- # parse_domain_name('Foo.Bar.Baz')
10
- # ['Foo', 'Bar', 'Foo.Bar.Baz']
10
+ # parse_domain_name('foo.bar')
11
+ # ['foo', 'bar', 'foo.bar']
12
+ #
11
13
  # @param name [String, Object] domain name
12
14
  # @return [Array] parsed Array from domain name
13
15
  def parse_domain_name(name)
@@ -18,7 +20,7 @@ module Appfuel
18
20
  name = name.domain_name if name.respond_to?(:domain_name)
19
21
  feature, domain = name.split('.')
20
22
  if domain.nil?
21
- domain, feature = feature, nil
23
+ fail "domain names must be in the form of (<feature|global>.domain)"
22
24
  end
23
25
 
24
26
  [feature, domain, name]
@@ -26,9 +28,11 @@ module Appfuel
26
28
 
27
29
  # This parse the domain attributes string and returns an array with
28
30
  # two elements.
31
+ #
29
32
  # @example
30
- # parse_domain_attr('Foo.Bar.Baz')
31
- # ['Foo.Bar', 'Baz']
33
+ # parse_domain_attr('foo.bar.baz')
34
+ # ['foo.bar', 'baz']
35
+ #
32
36
  # @param name [String] domain name attributes
33
37
  # @return [Array] parsed Array from domain attributes
34
38
  def parse_domain_attr(name)
@@ -1,12 +1,5 @@
1
1
  require_relative "domain/domain_name_parser"
2
2
  require_relative "domain/dsl"
3
- require_relative "domain/expr"
4
- require_relative "domain/expr_conjunction"
5
3
  require_relative "domain/entity"
6
4
  require_relative "domain/value_object"
7
5
  require_relative "domain/entity_collection"
8
- require_relative "domain/criteria_settings"
9
- require_relative "domain/base_criteria"
10
- require_relative "domain/search_criteria"
11
- require_relative "domain/exists_criteria"
12
- require_relative "domain/expr_parser"
@@ -29,20 +29,14 @@ module Appfuel
29
29
  # @param inputs [Hash] inputs to be validated
30
30
  # @return [Response]
31
31
  def run(inputs = {}, container = Dry::Container.new)
32
- begin
33
- response = resolve_inputs(inputs)
34
- return response if response.failure?
35
- valid_inputs = response.ok
36
-
37
- resolve_dependencies(container)
38
- handler = self.new(container)
39
- result = handler.call(valid_inputs)
40
- result = create_response(result) unless response?(result)
41
- rescue RunError => e
42
- result = e.response
43
- rescue StandardError => e
44
- result = error(e)
45
- end
32
+ response = resolve_inputs(inputs)
33
+ return response if response.failure?
34
+ valid_inputs = response.ok
35
+
36
+ resolve_dependencies(container)
37
+ handler = self.new(container)
38
+ result = handler.call(valid_inputs)
39
+ result = create_response(result) unless response?(result)
46
40
 
47
41
  result
48
42
  end
@@ -2,10 +2,6 @@ module Appfuel
2
2
  module Db
3
3
  class Mapper < Appfuel::Repository::Mapper
4
4
 
5
- def search(domain_name, criteria, opts = {})
6
-
7
- end
8
-
9
5
  # Return qualified db column name from entity expression.
10
6
  #
11
7
  # @param expr [SpCore::Domain::Expr]
@@ -33,7 +29,11 @@ module Appfuel
33
29
  # @param entry [Repository::MappingEntry] optional
34
30
  # @return [Array] The first index is the expr string using ? for values
35
31
  # The second index is the actual value(s)
36
- def convert_expr(expr, entry = nil)
32
+ def convert_expr(expr, values = [], entry = nil)
33
+ if expr_conjunction?(expr)
34
+ return convert_conjunction(expr, values, entry)
35
+ end
36
+
37
37
  column = qualified_db_column(expr, entry)
38
38
  op = expr.op
39
39
  arg = case expr.op
@@ -42,7 +42,15 @@ module Appfuel
42
42
  else
43
43
  '?'
44
44
  end
45
- ["#{column} #{op} #{arg}", expr.value]
45
+
46
+ values << expr.value
47
+ ["#{column} #{op} #{arg}", values]
48
+ end
49
+
50
+ def convert_conjunction(expr, values = [], entry = nil)
51
+ left, values = convert_expr(expr.left, values, entry)
52
+ right, values = convert_expr(expr.right, values, entry)
53
+ ["#{left} #{expr.op} #{right}", values]
46
54
  end
47
55
 
48
56
  # Validates if a record exists in the table that matches the array with
@@ -55,10 +63,10 @@ module Appfuel
55
63
  domain_attr = domain_expr.domain_attr
56
64
 
57
65
  entry = find(domain_name, domain_attr)
58
- db_expr, values = convert_expr(domain_expr, entry)
66
+ db_expr, values = convert_expr(domain_expr, [], entry)
59
67
  db = storage_class_from_entry(entry, :db)
60
68
 
61
- db.exists?([db_expr, values])
69
+ db.exists?([db_expr, *values])
62
70
  end
63
71
 
64
72
  # Build a where expression from the mapped db class using the criteria.Ï
@@ -67,18 +75,8 @@ module Appfuel
67
75
  # @param relation [DbModel, ActiveRecord::Relation]
68
76
  # @return [DbModel, ActiveRecord::Relation]
69
77
  def where(criteria, relation)
70
- unless criteria.where?
71
- fail "you must explicitly call :all when criteria has no exprs."
72
- end
73
-
74
- criteria.each do |domain_expr, op|
75
- relation = if op == :or
76
- relation.or(db_where(domain_expr, relation))
77
- else
78
- db_where(domain_expr, relation)
79
- end
80
- end
81
- relation
78
+ conditions, values = convert_expr(criteria.filters)
79
+ relation.where(conditions, *values)
82
80
  end
83
81
 
84
82
  # Build an order by expression for the given db relation based on the
@@ -89,12 +87,19 @@ module Appfuel
89
87
  # @return [ActiveRecord::Relation]
90
88
  def order(criteria, relation)
91
89
  return relation unless criteria.order?
92
- criteria.order.each do |expr|
90
+ order_str = convert_order_exprs(criteria.order_by)
91
+ relation.order(order_str)
92
+ end
93
+
94
+ def convert_order_exprs(list)
95
+ str = ''
96
+ list.each do |expr|
93
97
  db_column = qualified_db_column(expr)
94
- direction = expr.value
95
- relation = relation.order("#{db_column} #{direction}")
98
+ direction = expr.op
99
+ str << "#{db_column} #{direction}, "
96
100
  end
97
- relation
101
+
102
+ str.strip.chomp(',')
98
103
  end
99
104
 
100
105
  # Eventhough there is no mapping here we add the interface for
@@ -109,17 +114,8 @@ module Appfuel
109
114
  relation.limit(criteria.limit)
110
115
  end
111
116
 
112
- # Map the entity expr to a hash of db_column => value and call
113
- # on the relation using that.
114
- #
115
- # @note this is db library specific and needs to be moved to an adapter
116
- #
117
- # @param expr [Appfuel::Domain::Expr]
118
- # @param relation [ActiveRecord::Relation]
119
- # @return [ActiveRecord::Relation]
120
- def db_where(domain_expr, relation)
121
- db_expr = create_db_expr(domain_expr)
122
- relation.where([db_expr.string, db_expr.values])
117
+ def storage_hash(db_model)
118
+ db_model.attributes.select {|_, value| !value.nil?}
123
119
  end
124
120
  end
125
121
  end
@@ -24,35 +24,14 @@ module Appfuel
24
24
  build(name: entity.domain_name, storage: db_results, type: :db)
25
25
  end
26
26
 
27
- def db_class(key)
28
- app_container[key]
29
- end
30
-
31
- # Used when the automated query methods don't suite your use case. It
32
- # is assumed that the method executed will honor the same interfaces as
33
- # query does
34
- #
35
- # @param criteria [SpCore::Criteria]
36
- # @return [Dataset]
37
- def execute_criteria(criteria)
38
- query_method = "#{criteria.exec}_manual_query"
39
- validate_query_method(query_method)
40
-
41
- public_send(query_method, criteria)
42
- end
43
-
44
- # Use the criteria entity's basename as a convention to find a method
45
- # on the repository that returns the necessary relation (db scope) for
46
- # which to add conditions that will be used to complete the query.
27
+ # when key has no . assume the feature of the repository
47
28
  #
48
29
  #
49
- # @param criteria [SpCore::Criteria]
50
- # @return [ActiveRecord::Relation]
51
- def query_relation(criteria)
52
- query_method = "#{criteria.domain}_query"
53
- validate_query_method(query_method)
54
-
55
- public_send(query_method)
30
+ def db_class(key)
31
+ unless key.include?('.')
32
+ key = "features.#{self.class.container_feature_name}.db.#{key}"
33
+ end
34
+ app_container[key]
56
35
  end
57
36
 
58
37
  # Handles the treatment of the relation when the recordset is empty
@@ -86,126 +65,79 @@ module Appfuel
86
65
  # @param criteria [SpCore::Criteria]
87
66
  # @param relation [mixed]
88
67
  # @return relation
89
- def apply_query_conditions(criteria, relation)
90
- relation = where(criteria, relation)
91
- relation = order(criteria, relation)
92
- relation = limit(criteria, relation)
68
+ def apply_query_conditions(criteria, relation, _settings)
69
+ ap 'i am applying some query condititions'
70
+ relation = mapper.where(criteria, relation)
71
+ ap relation
72
+ ap 'some where conditions'
73
+ relation = mapper.order(criteria, relation)
74
+ ap 'some order condition'
75
+ relation = mapper.limit(criteria, relation)
76
+ ap 'some limit condition'
93
77
  relation
94
78
  end
95
79
 
96
- # We have an interface for getting all recordsets separately because
97
- # this is usually done with no filters or limits.
98
- #
99
- # @param criteria [SpCore::Criteria]
100
- # @param relation
101
- # @return relation
102
- def apply_query_all(criteria, relation)
103
- unless criteria.all?
104
- fail "This interface can only be used when the criteria :all is used"
105
- end
106
-
107
- order(criteria, relation.all)
108
- end
109
-
110
80
  # Determines which query conditions to apply to the relation
111
81
  #
112
82
  # @param criteria [SpCore::Criteria]
113
83
  # @param relation
114
84
  # @return relation
115
- def handle_query_conditions(criteria, relation)
116
- if criteria.all?
117
- return apply_query_all(criteria, relation)
85
+ def handle_query_conditions(criteria, relation, settings)
86
+ if settings.all?
87
+ return order(criteria, relation.all)
118
88
  end
119
89
 
120
- apply_query_conditions(criteria, relation)
90
+ apply_query_conditions(criteria, relation, settings)
121
91
  end
122
92
 
123
- # Factory method to create a domain entity
124
- #
125
- # @param domain_name [String]
126
- # @return [SpCore::Domain::EntityCollection]
127
- def create_entity_collection(domain_name)
128
- Appfuel::Domain::EntityCollection.new(domain_name)
129
- end
93
+ def handle_empty_relation(criteria, relation, settings)
94
+ unless relation.respond_to?(:blank?)
95
+ fail "The database relation invalid, does not implement :blank?"
96
+ end
130
97
 
131
- # Creates a lambda to used with the entity collection
132
- #
133
- # @param criteria [SpCore::Criteria]
134
- # @param relation [Object]
135
- # @param builder [Object]
136
- # @return lambda
137
- def entity_loader(criteria, relation, builder)
138
- -> { load_collection(criteria, relation, builder) }
139
- end
98
+ return nil unless relation.blank?
140
99
 
141
- # A collection is usually loaded within an entity collection via
142
- # a lambda. It setups up pagination results and builds an entity
143
- # foreach record in the list
144
- #
145
- # @param criteria [SpCore::Criteria]
146
- # @param relation [Object]
147
- # @param builder [Object]
148
- # @return [Hash]
149
- def load_collection(criteria, relation, builder)
150
- data = { items: [] }
151
- unless criteria.disable_pagination?
152
- relation = relation.page(criteria.page).per(criteria.per_page)
153
- data[:pager] = create_pager_result(
154
- total_pages: relation.total_pages,
155
- current_page: relation.current_page,
156
- total_count: relation.total_count,
157
- page_limit: relation.limit_value,
158
- page_size: relation.size
159
- )
100
+ if criteria.error_on_empty_dataset?
101
+ return domain_not_found_error(criteria)
160
102
  end
161
103
 
162
- relation.each do |db_item|
163
- data[:items] << builder.call(db_item)
104
+ if criteria.single?
105
+ return domain_not_found(criteria)
164
106
  end
165
- data
166
107
  end
167
108
 
168
- # Create an entity collection and assign the entity loader with
169
- # the entity builder class.
109
+ # 1) lookup query id in cache
110
+ # if found build collection from cached query ids
170
111
  #
171
- # @param criteria [SpCore::Criteria]
172
- # @param relation relation
173
- # @return SpCore::Domain::EntityCollection
174
- def build_criteria_entities(criteria, relation)
175
- builder = create_entity_builder(criteria.domain_name)
176
- result = handle_empty_relation(criteria, relation)
177
- # when this has a result it means an empty relation has been
178
- # handled and ready for return otherwise it was a no op
112
+ # 2) query id not found in cache
113
+ # a) assign query id from criteria
114
+ # b) find the domain builder for that criteria
115
+ # c) loop through each item in the database relation
116
+ # d) build an domain from each record in the relation
117
+ # e) create cache id from the domain
118
+ # f) record cache id into a list that represents query
119
+ # g) assign domain into the cache with its cache id
120
+ # id is in the cache the its updated *represents a miss
121
+ # h) assign the query list into the cache with its query id
122
+ #
123
+ def build_domains(criteria, relation, settings)
124
+ result = handle_empty_relation(criteria, relation, settings)
179
125
  return result if result
180
126
 
181
- if criteria.single?
182
- relation_method = criteria.first? ? :first : :last
183
- return builder.call(relation.send(relation_method))
127
+
128
+ if settings.single?
129
+ method = settings.first? ? :first : :last
130
+ db_model = relation.send(method)
131
+ builder = domain_builder(criteria.domain_name)
132
+ domain = builder.call(db_model, criteria)
133
+ ap domain
184
134
  end
185
135
 
186
- collection = create_entity_collection(criteria.domain_name)
187
- collection.entity_loader = entity_loader(criteria, relation, builder)
188
- collection
189
136
  end
190
137
 
191
- # Query will use the database model to build a query based on the
192
- # given criteria. It supports where, order and limit conditions.
193
- #
194
- # @param criteria [SpCore::Criteria]
195
- # @return [SpCore::Domain::Entity, SpCore::Domain::EntityCollection]
196
- def search(criteria)
197
- return execute_criteria(criteria) if criteria.exec?
198
-
199
- begin
200
- relation = query_relation(criteria)
201
- relation = handle_query_conditions(criteria, relation)
202
- build_criteria_entities(criteria, relation)
203
- rescue => e
204
- msg = "query failed for #{criteria.domain}: #{e.class} #{e.message}"
205
- err = RuntimeError.new(msg)
206
- err.set_backtrace(e.backtrace)
207
- raise err
208
- end
138
+ def domain_builder(domain_name)
139
+ key = qualify_container_key(domain_name, 'domain_builders.db')
140
+ app_container[key]
209
141
  end
210
142
 
211
143
  private