shamu 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +26 -0
  3. data/.gitignore +2 -1
  4. data/.rubocop.yml +89 -30
  5. data/.yardopts +4 -5
  6. data/Gemfile +24 -12
  7. data/Guardfile +5 -0
  8. data/LABELS.md +22 -0
  9. data/README.md +41 -0
  10. data/Rakefile +12 -0
  11. data/circle.yml +7 -3
  12. data/config.ru +7 -0
  13. data/lib/shamu/active_record.rb +7 -0
  14. data/lib/shamu/attributes/assignment.rb +114 -0
  15. data/lib/shamu/attributes/equality.rb +40 -0
  16. data/lib/shamu/attributes/fluid_assignment.rb +49 -0
  17. data/lib/shamu/attributes/validation.rb +74 -0
  18. data/lib/shamu/attributes.rb +255 -0
  19. data/lib/shamu/auditing/README.md +0 -0
  20. data/lib/shamu/auditing/audit_record.rb +32 -0
  21. data/lib/shamu/auditing/auditing_service.rb +32 -0
  22. data/lib/shamu/auditing/list_scope.rb +22 -0
  23. data/lib/shamu/auditing/logging_auditing_service.rb +16 -0
  24. data/lib/shamu/auditing/support.rb +75 -0
  25. data/lib/shamu/auditing/transaction.rb +58 -0
  26. data/lib/shamu/auditing.rb +12 -0
  27. data/lib/shamu/entities/README.md +1 -0
  28. data/lib/shamu/entities/active_record.rb +123 -0
  29. data/lib/shamu/entities/active_record_soft_destroy.rb +91 -0
  30. data/lib/shamu/entities/entity.rb +196 -0
  31. data/lib/shamu/entities/entity_path.rb +87 -0
  32. data/lib/shamu/entities/identity_cache.rb +64 -0
  33. data/lib/shamu/entities/list.rb +54 -0
  34. data/lib/shamu/entities/list_scope/dates.rb +57 -0
  35. data/lib/shamu/entities/list_scope/paging.rb +51 -0
  36. data/lib/shamu/entities/list_scope/scoped_paging.rb +65 -0
  37. data/lib/shamu/entities/list_scope/sorting.rb +76 -0
  38. data/lib/shamu/entities/list_scope.rb +105 -0
  39. data/lib/shamu/entities/null_entity.rb +88 -0
  40. data/lib/shamu/entities.rb +11 -0
  41. data/lib/shamu/error.rb +23 -5
  42. data/lib/shamu/events/README.md +0 -0
  43. data/lib/shamu/events/active_record/channel.rb +36 -0
  44. data/lib/shamu/events/active_record/message.rb +52 -0
  45. data/lib/shamu/events/active_record/migration.rb +49 -0
  46. data/lib/shamu/events/active_record/runner.rb +28 -0
  47. data/lib/shamu/events/active_record/service.rb +174 -0
  48. data/lib/shamu/events/active_record.rb +13 -0
  49. data/lib/shamu/events/channel_stats.rb +23 -0
  50. data/lib/shamu/events/error.rb +24 -0
  51. data/lib/shamu/events/events_service.rb +136 -0
  52. data/lib/shamu/events/in_memory/async_service.rb +48 -0
  53. data/lib/shamu/events/in_memory/service.rb +97 -0
  54. data/lib/shamu/events/in_memory.rb +10 -0
  55. data/lib/shamu/events/message.rb +38 -0
  56. data/lib/shamu/events/support.rb +60 -0
  57. data/lib/shamu/events.rb +12 -0
  58. data/lib/shamu/features/README.md +0 -0
  59. data/lib/shamu/features/conditions/condition.rb +39 -0
  60. data/lib/shamu/features/conditions/env.rb +37 -0
  61. data/lib/shamu/features/conditions/hosts.rb +25 -0
  62. data/lib/shamu/features/conditions/matching.rb +16 -0
  63. data/lib/shamu/features/conditions/not_matching.rb +16 -0
  64. data/lib/shamu/features/conditions/percentage.rb +44 -0
  65. data/lib/shamu/features/conditions/proc.rb +54 -0
  66. data/lib/shamu/features/conditions/roles.rb +23 -0
  67. data/lib/shamu/features/conditions/schedule_at.rb +27 -0
  68. data/lib/shamu/features/conditions.rb +18 -0
  69. data/lib/shamu/features/config_service.rb +10 -0
  70. data/lib/shamu/features/context.rb +80 -0
  71. data/lib/shamu/features/env_store.rb +88 -0
  72. data/lib/shamu/features/errors.rb +29 -0
  73. data/lib/shamu/features/features_service.rb +168 -0
  74. data/lib/shamu/features/list_scope.rb +30 -0
  75. data/lib/shamu/features/selector.rb +50 -0
  76. data/lib/shamu/features/support.rb +51 -0
  77. data/lib/shamu/features/toggle.rb +149 -0
  78. data/lib/shamu/features/toggle_codec.rb +69 -0
  79. data/lib/shamu/features.rb +16 -0
  80. data/lib/shamu/locale/en.yml +22 -2
  81. data/lib/shamu/logger.rb +13 -0
  82. data/lib/shamu/rack/README.md +0 -0
  83. data/lib/shamu/rack/cookies.rb +115 -0
  84. data/lib/shamu/rack/cookies_middleware.rb +26 -0
  85. data/lib/shamu/rack/query_params.rb +41 -0
  86. data/lib/shamu/rack/query_params_middleware.rb +24 -0
  87. data/lib/shamu/rack.rb +12 -0
  88. data/lib/shamu/rails/controller.rb +131 -0
  89. data/lib/shamu/rails/entity.rb +168 -0
  90. data/lib/shamu/rails/features.rb +13 -0
  91. data/lib/shamu/rails/railtie.rb +30 -0
  92. data/lib/shamu/rails.rb +10 -0
  93. data/lib/shamu/rspec/matchers.rb +44 -0
  94. data/lib/shamu/rspec.rb +1 -0
  95. data/lib/shamu/security/README.md +0 -0
  96. data/lib/shamu/security/active_record_policy.rb +106 -0
  97. data/lib/shamu/security/error.rb +65 -0
  98. data/lib/shamu/security/hashed_value.rb +71 -0
  99. data/lib/shamu/security/no_policy.rb +15 -0
  100. data/lib/shamu/security/policy.rb +289 -0
  101. data/lib/shamu/security/policy_refinement.rb +50 -0
  102. data/lib/shamu/security/policy_rule.rb +59 -0
  103. data/lib/shamu/security/principal.rb +72 -0
  104. data/lib/shamu/security/roles.rb +62 -0
  105. data/lib/shamu/security/roles_service.rb +30 -0
  106. data/lib/shamu/security/support.rb +83 -0
  107. data/lib/shamu/security.rb +43 -0
  108. data/lib/shamu/services/README.md +2 -0
  109. data/lib/shamu/services/active_record.rb +58 -0
  110. data/lib/shamu/services/active_record_crud.rb +378 -0
  111. data/lib/shamu/services/error.rb +24 -0
  112. data/lib/shamu/services/lazy_association.rb +31 -0
  113. data/lib/shamu/services/lazy_transform.rb +97 -0
  114. data/lib/shamu/services/request.rb +122 -0
  115. data/lib/shamu/services/request_support.rb +124 -0
  116. data/lib/shamu/services/result.rb +75 -0
  117. data/lib/shamu/services/service.rb +355 -0
  118. data/lib/shamu/services.rb +12 -0
  119. data/lib/shamu/sessions/README.md +2 -0
  120. data/lib/shamu/sessions/cookie_store.rb +79 -0
  121. data/lib/shamu/sessions/session_store.rb +42 -0
  122. data/lib/shamu/sessions.rb +8 -0
  123. data/lib/shamu/to_bool_extension.rb +57 -0
  124. data/lib/shamu/to_model_id_extension.rb +50 -0
  125. data/lib/shamu/version.rb +10 -4
  126. data/lib/shamu.rb +18 -6
  127. data/shamu.gemspec +21 -10
  128. data/spec/internal/README.md +4 -0
  129. data/spec/internal/config/database.yml +3 -0
  130. data/spec/internal/config/routes.rb +3 -0
  131. data/spec/internal/db/schema.rb +3 -0
  132. data/spec/internal/log/.gitignore +1 -0
  133. data/spec/internal/public/favicon.ico +0 -0
  134. data/spec/lib/shamu/active_record_support.rb +32 -0
  135. data/spec/lib/shamu/attributes/assignment_spec.rb +129 -0
  136. data/spec/lib/shamu/attributes/equality_spec.rb +63 -0
  137. data/spec/lib/shamu/attributes/fluid_assignment_spec.rb +31 -0
  138. data/spec/lib/shamu/attributes/validation_spec.rb +53 -0
  139. data/spec/lib/shamu/attributes_spec.rb +331 -0
  140. data/spec/lib/shamu/auditing/logging_auditing_service_spec.rb +18 -0
  141. data/spec/lib/shamu/auditing/support_spec.rb +41 -0
  142. data/spec/lib/shamu/entities/active_record_soft_destroy_spec.rb +82 -0
  143. data/spec/lib/shamu/entities/active_record_spec.rb +66 -0
  144. data/spec/lib/shamu/entities/entity_path_spec.rb +40 -0
  145. data/spec/lib/shamu/entities/entity_spec.rb +56 -0
  146. data/spec/lib/shamu/entities/identity_cache_spec.rb +69 -0
  147. data/spec/lib/shamu/entities/list_scope/dates_spec.rb +47 -0
  148. data/spec/lib/shamu/entities/list_scope/paging_spec.rb +41 -0
  149. data/spec/lib/shamu/entities/list_scope/scoped_paging_spec.rb +40 -0
  150. data/spec/lib/shamu/entities/list_scope/sorting_spec.rb +59 -0
  151. data/spec/lib/shamu/entities/list_scope_spec.rb +127 -0
  152. data/spec/lib/shamu/entities/list_spec.rb +60 -0
  153. data/spec/lib/shamu/entities/null_entity_spec.rb +94 -0
  154. data/spec/lib/shamu/events/active_record/migration_spec.rb +11 -0
  155. data/spec/lib/shamu/events/active_record/service_spec.rb +139 -0
  156. data/spec/lib/shamu/events/events_service_spec.rb +57 -0
  157. data/spec/lib/shamu/events/in_memory/async_service_spec.rb +37 -0
  158. data/spec/lib/shamu/events/in_memory/service_spec.rb +36 -0
  159. data/spec/lib/shamu/events/message_spec.rb +7 -0
  160. data/spec/lib/shamu/events/support_spec.rb +44 -0
  161. data/spec/lib/shamu/features/conditions/condition_spec.rb +8 -0
  162. data/spec/lib/shamu/features/conditions/env_spec.rb +29 -0
  163. data/spec/lib/shamu/features/conditions/hosts_spec.rb +21 -0
  164. data/spec/lib/shamu/features/conditions/matching_spec.rb +23 -0
  165. data/spec/lib/shamu/features/conditions/percentage_spec.rb +71 -0
  166. data/spec/lib/shamu/features/conditions/proc_spec.rb +28 -0
  167. data/spec/lib/shamu/features/env_store_spec.rb +48 -0
  168. data/spec/lib/shamu/features/features.yml +34 -0
  169. data/spec/lib/shamu/features/features_service_spec.rb +109 -0
  170. data/spec/lib/shamu/features/secondary.yml +5 -0
  171. data/spec/lib/shamu/features/selector_spec.rb +17 -0
  172. data/spec/lib/shamu/features/support_spec.rb +45 -0
  173. data/spec/lib/shamu/features/toggle_codec_spec.rb +28 -0
  174. data/spec/lib/shamu/features/toggle_spec.rb +42 -0
  175. data/spec/lib/shamu/rack/cookies_middleware_spec.rb +33 -0
  176. data/spec/lib/shamu/rack/cookies_spec.rb +43 -0
  177. data/spec/lib/shamu/rack/query_params_middleware_spec.rb +33 -0
  178. data/spec/lib/shamu/rack/query_params_spec.rb +23 -0
  179. data/spec/lib/shamu/rails/controller_spec.rb +74 -0
  180. data/spec/lib/shamu/rails/entity_spec.rb +150 -0
  181. data/spec/lib/shamu/rails/features.yml +13 -0
  182. data/spec/lib/shamu/rails/features_spec.rb +45 -0
  183. data/spec/lib/shamu/security/active_record_policy_spec.rb +38 -0
  184. data/spec/lib/shamu/security/hashed_value_spec.rb +41 -0
  185. data/spec/lib/shamu/security/policy_refinement_spec.rb +61 -0
  186. data/spec/lib/shamu/security/policy_rule_spec.rb +60 -0
  187. data/spec/lib/shamu/security/policy_spec.rb +158 -0
  188. data/spec/lib/shamu/security/roles_spec.rb +46 -0
  189. data/spec/lib/shamu/services/active_record_crud_spec.rb +460 -0
  190. data/spec/lib/shamu/services/active_record_spec.rb +92 -0
  191. data/spec/lib/shamu/services/lazy_association_spec.rb +31 -0
  192. data/spec/lib/shamu/services/lazy_transform_spec.rb +96 -0
  193. data/spec/lib/shamu/services/request_spec.rb +58 -0
  194. data/spec/lib/shamu/services/request_support_spec.rb +129 -0
  195. data/spec/lib/shamu/services/result_spec.rb +37 -0
  196. data/spec/lib/shamu/services/service_spec.rb +307 -0
  197. data/spec/lib/shamu/sessions/cookie_store_spec.rb +44 -0
  198. data/spec/lib/shamu/to_bool_extension_spec.rb +67 -0
  199. data/spec/lib/shamu/to_model_id_extension_spec.rb +54 -0
  200. data/spec/rails_helper.rb +13 -0
  201. data/spec/spec_helper.rb +17 -12
  202. data/spec/support/active_record.rb +17 -0
  203. data/spec/support/database.rb +14 -0
  204. data/spec/support/logger.rb +0 -0
  205. metadata +383 -9
  206. data/spec/lib/shamu_spec.rb +0 -5
  207. /data/{spec/internal/log/test.log → lib/shamu/attributes/README.md} +0 -0
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+ require "shamu/entities"
3
+
4
+ describe Shamu::Entities::ListScope::Dates do
5
+ let( :klass ) do
6
+ Class.new( Shamu::Entities::ListScope ) do
7
+ include Shamu::Entities::ListScope::Dates
8
+ end
9
+ end
10
+
11
+ it "has a :since attribute" do
12
+ expect( klass.attributes ).to have_key :since
13
+ end
14
+
15
+ it "has an :until attribute" do
16
+ expect( klass.attributes ).to have_key :until
17
+ end
18
+
19
+ it "coerces with #to_time if available" do
20
+ expect( Time ).to receive( :instance_method ).and_return( true )
21
+ value = double
22
+ expect( value ).to receive( :to_time )
23
+
24
+ klass.new( since: value )
25
+ end
26
+
27
+ it "includes paging values in to_param" do
28
+ time = Time.now
29
+ expect( klass.new( since: time, until: time ).params ).to eq since: time, until: time
30
+ end
31
+
32
+ it "should not be dated when using defaults" do
33
+ scope = klass.new
34
+ expect( scope.dated? ).to be_falsy
35
+ end
36
+
37
+ it "should not be dated when since is provided" do
38
+ scope = klass.new since: Time.now
39
+ expect( scope.dated? ).to be_truthy
40
+ end
41
+
42
+ it "should not be dated when until is provided" do
43
+ scope = klass.new until: Time.now
44
+ expect( scope.dated? ).to be_truthy
45
+ end
46
+
47
+ end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+ require "shamu/entities"
3
+
4
+ describe Shamu::Entities::ListScope::Paging do
5
+ let( :klass ) do
6
+ Class.new( Shamu::Entities::ListScope ) do
7
+ include Shamu::Entities::ListScope::Paging
8
+ end
9
+ end
10
+
11
+ it "has a :page attribute" do
12
+ expect( klass.attributes ).to have_key :page
13
+ end
14
+
15
+ it "has a :per_page attribute" do
16
+ expect( klass.attributes ).to have_key :per_page
17
+ end
18
+
19
+ it "has a :default_per_page" do
20
+ expect( klass.attributes ).to have_key :default_per_page
21
+ end
22
+
23
+ it "uses default_per_page if not per_page set" do
24
+ expect( klass.new.per_page ).to eq 25
25
+ end
26
+
27
+ it "includes paging values in to_param" do
28
+ expect( klass.new.params ).to eq page: nil, per_page: 25
29
+ end
30
+
31
+ it "should not be paged if using defaults" do
32
+ scope = klass.new
33
+ expect( scope.paged? ).to be_falsy
34
+ end
35
+
36
+ it "should be paged when attribute specified" do
37
+ scope = klass.new page: 1
38
+ expect( scope.paged? ).to be_truthy
39
+ end
40
+
41
+ end
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+ require "shamu/entities"
3
+
4
+ describe Shamu::Entities::ListScope::Paging do
5
+ let( :klass ) do
6
+ Class.new( Shamu::Entities::ListScope ) do
7
+ include Shamu::Entities::ListScope::ScopedPaging
8
+ end
9
+ end
10
+
11
+ it "has a :page attribute" do
12
+ expect( klass.attributes ).to have_key :page
13
+ end
14
+
15
+ it "has a :page.number attribute" do
16
+ expect( klass.new.page.class.attributes ).to have_key :number
17
+ end
18
+
19
+ it "has a :page.size attribute" do
20
+ expect( klass.new.page.class.attributes ).to have_key :size
21
+ end
22
+
23
+ it "has a :page.default_size attribute" do
24
+ expect( klass.new.page.class.attributes ).to have_key :default_size
25
+ end
26
+
27
+ it "includes paging values in to_param" do
28
+ expect( klass.new.params ).to eq page: { number: nil, size: 25 }
29
+ end
30
+
31
+ it "should be paged when attribute specified" do
32
+ scope = klass.new page: { number: 1 }
33
+ expect( scope.scoped_page? ).to be_truthy
34
+ end
35
+
36
+ it "should not be paged if using defaults" do
37
+ scope = klass.new
38
+ expect( scope.scoped_page? ).to be_falsy
39
+ end
40
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+ require "shamu/entities"
3
+
4
+ describe Shamu::Entities::ListScope::Paging do
5
+ let( :klass ) do
6
+ Class.new( Shamu::Entities::ListScope ) do
7
+ include Shamu::Entities::ListScope::Sorting
8
+ end
9
+ end
10
+
11
+ it "parses single values" do
12
+ scope = klass.new( sort_by: :first_name )
13
+ expect( scope.sort_by ).to eq first_name: :asc
14
+ end
15
+
16
+ it "parses array of values" do
17
+ scope = klass.new( sort_by: [ :first_name, :last_name ] )
18
+ expect( scope.sort_by ).to eq first_name: :asc, last_name: :asc
19
+ end
20
+
21
+ it "parses array of args to fluid_assignment" do
22
+ scope = klass.new
23
+ scope.sort_by :first_name, :last_name
24
+ expect( scope.sort_by ).to eq first_name: :asc, last_name: :asc
25
+ end
26
+
27
+ it "parses array via assignment" do
28
+ scope = klass.new
29
+ scope.sort_by = [ :first_name, :last_name ]
30
+ expect( scope.sort_by ).to eq first_name: :asc, last_name: :asc
31
+ end
32
+
33
+ it "parses hash" do
34
+ scope = klass.new sort_by: { first_name: :desc }
35
+ expect( scope.sort_by ).to eq first_name: :desc
36
+ end
37
+
38
+ it "parses array with hash" do
39
+ scope = klass.new sort_by: [{ last_name: :desc }]
40
+ expect( scope.sort_by ).to eq last_name: :desc
41
+ end
42
+
43
+ it "parses hash with array" do
44
+ scope = klass.new sort_by: { campaign: [ :created_at ] }
45
+ expect( scope.sort_by ).to eq campaign: { created_at: :asc }
46
+ end
47
+
48
+ it "includes sorting values in to_param" do
49
+ expect( klass.new( sort_by: :name ).params ).to eq sort_by: { name: :asc }
50
+ end
51
+
52
+ it "is not sorted with defaults" do
53
+ expect( klass.new.sorted? ).to be_falsy
54
+ end
55
+
56
+ it "is sorted when asked" do
57
+ expect( klass.new( sort_by: :name ).sorted? ).to be_truthy
58
+ end
59
+ end
@@ -0,0 +1,127 @@
1
+ require "spec_helper"
2
+ require "shamu/entities"
3
+
4
+ describe Shamu::Entities::ListScope do
5
+
6
+ let( :klass ) do
7
+ Class.new( Shamu::Entities::ListScope ) do
8
+ def self.validates( * ); end
9
+
10
+ attribute :name, presence: true
11
+ end
12
+ end
13
+
14
+ describe ".coerce" do
15
+ it "coerces an instance of the scope to self" do
16
+ scope = klass.new
17
+ expect( klass.coerce( scope ) ).to be scope
18
+ end
19
+
20
+ it "coerces a hash" do
21
+ expect( klass.coerce( {} ) ).to be_a klass
22
+ end
23
+
24
+ it "coerces a nil" do
25
+ expect( klass.coerce( nil ) ).to be_a klass
26
+ end
27
+
28
+ it "raises ArgumentError on other values" do
29
+ expect do
30
+ klass.coerce( "" )
31
+ end.to raise_error ArgumentError
32
+ end
33
+ end
34
+
35
+ describe ".coerce!" do
36
+ it "raises ArgumentError if the scope has invalid params" do
37
+ scope = klass.new( name: nil )
38
+ expect( scope ).to receive( :valid? ).and_return false
39
+
40
+ expect do
41
+ klass.coerce!( scope )
42
+ end.to raise_error ArgumentError
43
+ end
44
+
45
+ it "returns the scope if the params are valid" do
46
+ scope = klass.new( name: nil )
47
+ expect( scope ).to receive( :valid? ).and_return true
48
+
49
+ expect do
50
+ klass.coerce!( scope )
51
+ end.not_to raise_error
52
+ end
53
+ end
54
+
55
+ describe "#except" do
56
+ it "excludes the requested params" do
57
+ instance = klass.new( name: "Orca" )
58
+ expect( instance.except( :name ).name ).to be_nil
59
+ end
60
+
61
+ it "returns an instance of the same class" do
62
+ instance = klass.new( name: "Killer" )
63
+ expect( instance.except ).to be_a klass
64
+ end
65
+ end
66
+
67
+ describe "#params" do
68
+ it "calls params on nested values" do
69
+ name = "Nested"
70
+ instance = klass.new( name: name )
71
+ expect( name ).to receive( :params ).and_return "parameterized"
72
+ expect( instance.params[:name] ).to eq "parameterized"
73
+ end
74
+ end
75
+
76
+ describe ".for" do
77
+ let( :namespace ) do
78
+ Object.send( :remove_const, :EntityModule ) if defined? EntityModule
79
+ EntityModule = Module.new
80
+ end
81
+
82
+ describe "with 'Entity' class" do
83
+ let( :entity_class ) do
84
+ namespace.const_set :ResourceEntity, Class.new( Shamu::Entities::Entity )
85
+ end
86
+
87
+ it "tries Namespace::EntityNameListScope" do
88
+ klass = Class.new( Shamu::Entities::ListScope )
89
+ namespace.const_set :ResourceListScope, klass
90
+ expect( Shamu::Entities::ListScope.for( entity_class ) ).to be klass
91
+ end
92
+
93
+ it "tries Namespace::ListScope" do
94
+ klass = Class.new( Shamu::Entities::ListScope )
95
+ namespace.const_set :ListScope, klass
96
+ expect( Shamu::Entities::ListScope.for( entity_class ) ).to be klass
97
+ end
98
+
99
+ it "falls back to ListScope" do
100
+ expect( Shamu::Entities::ListScope.for( Shamu::Entities::Entity ) ).to be Shamu::Entities::ListScope
101
+ end
102
+ end
103
+
104
+ describe "with unadorned entity class" do
105
+ let( :entity_class ) do
106
+ namespace.const_set :Resource, Class.new( Shamu::Entities::Entity )
107
+ end
108
+
109
+ it "tries Namespace::EntityNameListScope" do
110
+ klass = Class.new( Shamu::Entities::ListScope )
111
+ namespace.const_set :ResourceListScope, klass
112
+ expect( Shamu::Entities::ListScope.for( entity_class ) ).to be klass
113
+ end
114
+
115
+ it "tries Namespace::ListScope" do
116
+ klass = Class.new( Shamu::Entities::ListScope )
117
+ namespace.const_set :ListScope, klass
118
+ expect( Shamu::Entities::ListScope.for( entity_class ) ).to be klass
119
+ end
120
+
121
+ it "falls back to ListScope" do
122
+ expect( Shamu::Entities::ListScope.for( Shamu::Entities::Entity ) ).to be Shamu::Entities::ListScope
123
+ end
124
+ end
125
+ end
126
+
127
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+ require "shamu/entities"
3
+
4
+ module EntityListSpec
5
+ class Entity < Shamu::Entities::Entity
6
+ attribute :id
7
+ attribute :label
8
+ end
9
+ end
10
+
11
+ describe Shamu::Entities::List do
12
+ let( :first ) { EntityListSpec::Entity.new id: 1, label: :one }
13
+ let( :second ) { EntityListSpec::Entity.new id: 2, label: :two }
14
+ let( :source ) { [ first, second ] }
15
+
16
+ let( :list ) do
17
+ Shamu::Entities::List.new( source )
18
+ end
19
+
20
+ describe "#each" do
21
+ it "is lazy" do
22
+ raw = [ first, second ]
23
+ expect( raw ).to receive( :lazy )
24
+
25
+ list = Shamu::Entities::List.new( raw )
26
+ list.to_a
27
+ end
28
+
29
+ it "enumerates over the entities" do
30
+ expect do |b|
31
+ list.each( &b )
32
+ end.to yield_control.twice
33
+ end
34
+ end
35
+
36
+ describe "#get" do
37
+ it "finds by id by default" do
38
+ expect( list.get( 1 ) ).to be first
39
+ end
40
+
41
+ it "finds by custom pk" do
42
+ expect( list.get( :two, field: :label ) ).to be second
43
+ end
44
+
45
+ it "raises when not found" do
46
+ expect do
47
+ list.get( 42 )
48
+ end.to raise_error Shamu::NotFoundError
49
+ end
50
+ end
51
+
52
+ describe "short-circuits" do
53
+ [ :first, :count, :empty? ].each do |method|
54
+ it "delegates #{ method } to entities" do
55
+ expect( source ).to receive( method ).and_call_original
56
+ list.send( method )
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,94 @@
1
+ require "spec_helper"
2
+ require "shamu/entities"
3
+
4
+ describe Shamu::Entities::NullEntity do
5
+ let( :klass ) do
6
+ Class.new( Shamu::Entities::Entity ) do
7
+ def self.name
8
+ "KillerWhale"
9
+ end
10
+
11
+ model :record
12
+
13
+ attribute :name, on: :record
14
+ attribute :email, on: :record
15
+ attribute :id, on: :record
16
+ attribute :group do
17
+ email.present? ? "Emailers" : "Quieters"
18
+ end
19
+ end
20
+ end
21
+
22
+ let( :null_klass ) do
23
+ Class.new( klass ) do
24
+ include Shamu::Entities::NullEntity
25
+
26
+ attribute :group, default: "Uncategorized"
27
+ end
28
+ end
29
+
30
+ let( :record ) do
31
+ OpenStruct.new \
32
+ name: "Killer",
33
+ email: "star@seaworld.com",
34
+ id: 5
35
+ end
36
+
37
+ let( :entity ) do
38
+ klass.new( record: record )
39
+ end
40
+
41
+ let( :null ) do
42
+ null_klass.new
43
+ end
44
+
45
+ describe "#empty?" do
46
+ it "is false for real entities" do
47
+ expect( entity ).not_to be_empty
48
+ end
49
+
50
+ it "is true for null entitites" do
51
+ expect( null ).to be_empty
52
+ end
53
+ end
54
+
55
+ describe "attributes" do
56
+ [ :title, :name, :label ].each do |attr|
57
+ it "automatically formats '#{ attr }' value with 'Unknown Entity'" do
58
+ auto_klass = Class.new( klass ) do
59
+ attribute attr
60
+ end
61
+
62
+ auto_null_klass = Class.new( auto_klass ) do
63
+ include Shamu::Entities::NullEntity
64
+ end
65
+
66
+ expect( auto_null_klass.new.send( attr ) ).to eq "Unknown Killer Whale"
67
+ end
68
+ end
69
+
70
+ it "does not automatically format other attributes" do
71
+ expect( null.email ).to be_nil
72
+ end
73
+
74
+ it "allow auto formatted to be overridden" do
75
+ auto_null_klass = Class.new( klass ) do
76
+ include Shamu::Entities::NullEntity
77
+
78
+ attribute :name, default: "Missing"
79
+ end
80
+
81
+ expect( auto_null_klass.new.name ).to eq "Missing"
82
+ end
83
+ end
84
+
85
+ describe ".for" do
86
+ it "creates a null class" do
87
+ expect( Shamu::Entities::NullEntity.for( klass ) ).to be < Shamu::Entities::NullEntity
88
+ end
89
+
90
+ it "inherits from the entity class" do
91
+ expect( Shamu::Entities::NullEntity.for( klass ) ).to be < klass
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,11 @@
1
+ require "spec_helper"
2
+ require "shamu/active_record"
3
+
4
+ describe Shamu::Events::ActiveRecord::Migration do
5
+
6
+ it "can run" do
7
+ Shamu::Events::ActiveRecord::Migration.new.migrate( :down )
8
+ Shamu::Events::ActiveRecord::Migration.new.migrate( :up )
9
+ end
10
+
11
+ end
@@ -0,0 +1,139 @@
1
+ require "spec_helper"
2
+ require "shamu/active_record"
3
+
4
+ describe Shamu::Events::ActiveRecord::Service do
5
+ let( :message ) { Shamu::Events::Message.new }
6
+ let( :service ) { scorpion.new Shamu::Events::ActiveRecord::Service }
7
+
8
+
9
+ before( :each ) do
10
+ Shamu::Events::ActiveRecord::Migration.new.migrate( :down )
11
+ Shamu::Events::ActiveRecord::Migration.new.migrate( :up )
12
+ end
13
+
14
+ it "prepares the database when initialized" do
15
+ expect( Shamu::Events::ActiveRecord::Service ).to receive( :ensure_records! ).and_call_original
16
+ scorpion.new Shamu::Events::ActiveRecord::Service
17
+ end
18
+
19
+ describe "#publish" do
20
+ it "persists the message" do
21
+ expect do
22
+ service.publish( "spec", message )
23
+ end.to change( Shamu::Events::ActiveRecord::Message, :count )
24
+ end
25
+
26
+ it "serializes the message" do
27
+ expect( service ).to receive( :serialize ).and_call_original
28
+ service.publish( "spec", message )
29
+ end
30
+
31
+ it "creates a new channel on first publish" do
32
+ puts Shamu::Events::ActiveRecord::Channel.all
33
+ expect do
34
+ service.publish "spec", message
35
+ end.to change( Shamu::Events::ActiveRecord::Channel, :count )
36
+ end
37
+
38
+ it "re-uses the same channel id for second publish" do
39
+ service.publish "spec", message
40
+
41
+ expect do
42
+ service.publish "spec", message
43
+ end.not_to change( Shamu::Events::ActiveRecord::Channel, :count )
44
+ end
45
+ end
46
+
47
+ describe "#subscribe" do
48
+ it "receives published messages" do
49
+ expect do |b|
50
+ service.publish "spec", message
51
+ service.subscribe "spec", &b
52
+ service.dispatch "specs", "spec"
53
+ end.to yield_control
54
+ end
55
+ end
56
+
57
+ describe "#dispatch" do
58
+ before( :each ) do
59
+ service.publish "spec", message
60
+ end
61
+
62
+ it "keeps track of last processed message" do
63
+ runner = Shamu::Events::ActiveRecord::Runner.create!( id: "specs::spec" )
64
+
65
+ expect do
66
+ service.dispatch "specs", "spec"
67
+ end.to change { runner.reload.last_processed_id }
68
+ end
69
+
70
+ it "keeps track of last run" do
71
+ runner = Shamu::Events::ActiveRecord::Runner.create!( id: "specs::spec" )
72
+
73
+ expect do
74
+ service.dispatch "specs", "spec"
75
+ end.to change { runner.reload.last_processed_at }
76
+ end
77
+
78
+ it "creates a unique id for each channel" do
79
+ service.dispatch "specs", "spec"
80
+
81
+ expect( Shamu::Events::ActiveRecord::Runner.first.id ).to eq "specs::spec"
82
+ end
83
+
84
+ it "dispatches new message since last run" do
85
+
86
+ service.dispatch "specs", "spec"
87
+ service.publish "spec", Shamu::Events::Message.new
88
+
89
+ expect do |b|
90
+ service.subscribe "spec", &b
91
+ service.dispatch "specs", "spec"
92
+ end.to yield_control.once
93
+ end
94
+
95
+
96
+ it "limits number of messages" do
97
+ 5.times do
98
+ service.publish "spec", Shamu::Events::Message.new
99
+ end
100
+
101
+ expect do |b|
102
+ service.subscribe "spec", &b
103
+ service.dispatch "specs", "spec", limit: 3
104
+ end.to yield_control.exactly(3)
105
+ end
106
+
107
+ it "returns the number of messages dispatched" do
108
+ expect( service.dispatch( "specs", "spec" )["spec"] ).to eq 1
109
+ end
110
+ end
111
+
112
+ describe "#channel_stats" do
113
+ before( :each ) do
114
+ service.publish( "spec", message )
115
+ end
116
+
117
+ subject { service.channel_stats( "spec" ) }
118
+
119
+ its( [:name] ) { is_expected.to eq "spec" }
120
+ its( [:subscribers_count] ) { is_expected.to eq 0 }
121
+ its( [:dispatching] ) { is_expected.to be_falsy }
122
+ its( [:queue_size] ) { is_expected.to eq 1 }
123
+
124
+ context "with runner" do
125
+ before( :each ) do
126
+ service.dispatch( "specs", "spec" )
127
+ end
128
+
129
+ subject { service.channel_stats( "spec", runner_id: "specs" ) }
130
+
131
+ its( [:name] ) { is_expected.to eq "spec" }
132
+ its( [:subscribers_count] ) { is_expected.to eq 0 }
133
+ its( [:dispatching] ) { is_expected.to be_falsy }
134
+ its( [:queue_size] ) { is_expected.to eq 0 }
135
+
136
+ end
137
+ end
138
+
139
+ end
@@ -0,0 +1,57 @@
1
+ require "spec_helper"
2
+
3
+ describe Shamu::Events::EventsService do
4
+ let( :klass ) do
5
+ Class.new( Shamu::Events::EventsService ) do
6
+ public :serialize, :deserialize
7
+ end
8
+ end
9
+ let( :service ) { scorpion.new klass }
10
+ let( :message ) { Shamu::Events::Message.new }
11
+
12
+ it "uses in-memory for default implementation" do
13
+ expect( scorpion.fetch( Shamu::Events::EventsService ) ).to be_a Shamu::Events::InMemory::Service
14
+ end
15
+
16
+ it "gets the same service for each default scorpion resolution" do
17
+ service = scorpion.fetch( Shamu::Events::EventsService )
18
+ expect( scorpion.fetch( Shamu::Events::EventsService ) ).to be service
19
+ end
20
+
21
+ describe "#serialize" do
22
+ it "generates a string" do
23
+ expect( service.serialize( message ) ).to be_a String
24
+ end
25
+ end
26
+
27
+ describe "#deserialize" do
28
+ it "creates a Message from a string" do
29
+ data = service.serialize( message )
30
+ deserialized = service.deserialize( data )
31
+
32
+ expect( deserialized ).to be_a message.class
33
+ expect( deserialized.id ).to eq message.id
34
+ end
35
+ end
36
+
37
+ describe ".bridge" do
38
+ let( :message ) { Shamu::Events::Message.new }
39
+
40
+ let( :source_service ) { scorpion.fetch Shamu::Events::InMemory::Service }
41
+ let( :target_service ) { double Shamu::Events::EventsService }
42
+
43
+ before( :each ) do
44
+ Shamu::Events::EventsService.bridge \
45
+ source_service,
46
+ target_service,
47
+ "spec"
48
+ end
49
+
50
+ it "forwards messages" do
51
+ expect( target_service ).to receive( :publish )
52
+
53
+ source_service.publish "spec", message
54
+ source_service.dispatch
55
+ end
56
+ end
57
+ end