houston-core 0.8.4 → 0.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +72 -78
  4. data/app/assets/javascripts/houston/app/models/role.coffee +4 -0
  5. data/app/assets/javascripts/houston/app/views/nested_resources.coffee +44 -0
  6. data/app/assets/javascripts/houston/app/views/team_roles_view.coffee +10 -0
  7. data/app/assets/javascripts/houston/application.js +3 -1
  8. data/app/assets/javascripts/houston/core/ajax_helpers.coffee +26 -0
  9. data/app/assets/javascripts/houston/core/app.coffee +0 -45
  10. data/app/assets/javascripts/houston/core/errors.coffee +1 -12
  11. data/app/assets/javascripts/houston/core/handlebars_helpers.coffee +8 -70
  12. data/app/assets/javascripts/houston/core/jquery_extensions.coffee +0 -138
  13. data/app/assets/javascripts/houston/core/timeline_helpers.coffee +44 -0
  14. data/app/assets/javascripts/houston/core/uploader_helpers.coffee +99 -0
  15. data/app/assets/javascripts/houston/vendor.js +26 -9
  16. data/app/assets/stylesheets/houston/application/actions.scss +22 -0
  17. data/app/assets/stylesheets/houston/application/navigation.scss +2 -2
  18. data/app/assets/stylesheets/houston/application/{freight_train.css.scss → nested_resources.scss} +1 -0
  19. data/app/assets/stylesheets/houston/core/alerts.scss +0 -4
  20. data/app/assets/stylesheets/houston/core/timeline.scss +204 -0
  21. data/app/assets/templates/houston/teams/roles/index.hbs +1 -0
  22. data/app/assets/templates/houston/teams/roles/show.hbs +19 -0
  23. data/app/channels/events_channel.rb +1 -1
  24. data/app/concerns/houston/props.rb +3 -2
  25. data/app/controllers/actions_controller.rb +8 -3
  26. data/app/controllers/application_controller.rb +9 -12
  27. data/app/controllers/authorizations_controller.rb +41 -14
  28. data/app/controllers/errors_controller.rb +3 -3
  29. data/app/controllers/project_follows_controller.rb +23 -0
  30. data/app/controllers/project_options_controller.rb +1 -1
  31. data/app/controllers/user_options_controller.rb +1 -1
  32. data/app/helpers/actions_helper.rb +12 -0
  33. data/app/helpers/application_helper.rb +0 -10
  34. data/app/helpers/layout_helper.rb +20 -0
  35. data/app/helpers/markdown_helper.rb +2 -10
  36. data/app/helpers/navigation_helper.rb +5 -5
  37. data/app/helpers/project_helper.rb +6 -0
  38. data/app/helpers/url_helper.rb +4 -4
  39. data/app/helpers/view_extensions_helper.rb +20 -0
  40. data/app/models/action.rb +61 -44
  41. data/app/models/authorization.rb +55 -10
  42. data/app/models/follow.rb +6 -0
  43. data/app/models/persistent_trigger.rb +11 -1
  44. data/app/models/project.rb +5 -27
  45. data/app/models/user.rb +15 -59
  46. data/app/presenters/project_presenter.rb +2 -2
  47. data/app/views/actions/_actions.html.erb +8 -5
  48. data/app/views/actions/index.html.erb +1 -1
  49. data/app/views/actions/running.html.erb +9 -15
  50. data/app/views/actions/show.html.erb +6 -3
  51. data/app/views/actions/unqueued.html.erb +41 -0
  52. data/app/views/authorizations/_form.html.erb +28 -16
  53. data/app/views/authorizations/index.html.erb +9 -4
  54. data/app/views/{oauth/providers/edit.html.erb → authorizations/oauth2_callback.html.erb} +3 -2
  55. data/app/views/devise/sessions/new.html.erb +0 -8
  56. data/app/views/errors/index.html.erb +9 -5
  57. data/app/views/layouts/_navigation.html.erb +9 -0
  58. data/app/views/layouts/application.html.erb +11 -6
  59. data/app/views/layouts/dashboard.html.erb +9 -0
  60. data/app/views/projects/_form.html.erb +7 -18
  61. data/app/views/projects/_header.html.erb +2 -6
  62. data/app/views/projects/index.html.erb +4 -4
  63. data/app/views/teams/_form.html.erb +7 -17
  64. data/app/views/teams/index.html.erb +1 -1
  65. data/app/views/users/_form.html.erb +1 -38
  66. data/config/application.rb +8 -5
  67. data/config/initializers/devise.rb +0 -14
  68. data/config/initializers/secret_token.rb +8 -13
  69. data/config/routes.rb +12 -28
  70. data/db/migrate/20130706141443_drop_deprecated_project_roles.rb +5 -1
  71. data/db/migrate/20170118005958_remove_antecedents_from_versions_of_tickets.rb +1 -1
  72. data/db/migrate/20170130011016_drop_users_environments_subscribed_to.rb +9 -0
  73. data/db/migrate/20170205004452_drop_settings.rb +12 -0
  74. data/db/migrate/20170206002030_drop_extension_hstore.rb +9 -0
  75. data/db/migrate/20170206002732_drop_legacy_columns_from_users.rb +10 -0
  76. data/db/migrate/20170209022159_rename_projects_color_to_color_name.rb +5 -0
  77. data/db/migrate/20170213001453_change_providers_from_models_to_extensions.rb +27 -0
  78. data/db/migrate/20170215012012_add_props_to_authorizations.rb +5 -0
  79. data/db/migrate/20170216041034_add_user_to_persistent_triggers.rb +5 -0
  80. data/db/migrate/20170226201504_create_follows.rb +20 -0
  81. data/db/migrate/20170301014051_drop_name_from_authorizations.rb +9 -0
  82. data/db/migrate/20170307032041_add_created_at_to_actions.rb +11 -0
  83. data/db/migrate/20170307035755_allow_actions_started_at_to_be_null.rb +5 -0
  84. data/db/migrate/20170310024505_replace_authorizations_provider_name_with_type.rb +12 -0
  85. data/db/migrate/20170329030329_drop_consumer_tokens.rb +9 -0
  86. data/db/structure.sql +187 -212
  87. data/houston-core.gemspec +10 -13
  88. data/lib/houston/boot.rb +1 -4
  89. data/lib/houston/boot/actions.rb +24 -21
  90. data/lib/houston/boot/configuration.rb +46 -113
  91. data/lib/houston/boot/extensions.rb +54 -341
  92. data/lib/houston/boot/extensions/deprecated.rb +194 -0
  93. data/lib/houston/boot/extensions/dsl.rb +99 -0
  94. data/lib/houston/boot/extensions/events.rb +81 -0
  95. data/lib/houston/boot/extensions/features.rb +42 -0
  96. data/lib/houston/boot/extensions/layout.rb +70 -0
  97. data/lib/houston/boot/extensions/navigation.rb +42 -0
  98. data/lib/houston/boot/extensions/oauth.rb +62 -0
  99. data/lib/houston/boot/extensions/serializers.rb +29 -0
  100. data/lib/houston/boot/extensions/view.rb +34 -0
  101. data/lib/houston/boot/observer.rb +10 -5
  102. data/{app/models/oauth → lib/houston/boot}/provider.rb +7 -5
  103. data/lib/houston/boot/running_as.rb +0 -5
  104. data/lib/houston/boot/serializer.rb +12 -6
  105. data/lib/houston/boot/{active_record_serializer.rb → serializers/active_record_serializer.rb} +0 -2
  106. data/lib/houston/boot/{readonly_hash_serializer.rb → serializers/readonly_hash_serializer.rb} +0 -2
  107. data/lib/houston/boot/timer.rb +10 -0
  108. data/lib/houston/boot/triggers.rb +27 -8
  109. data/lib/houston/version.rb +1 -1
  110. data/templates/new-instance/.gitignore +0 -4
  111. data/templates/new-instance/config/main.rb +8 -10
  112. data/templates/new-module/test/dummy/houston.rb +1 -0
  113. data/test/acceptance/layout_test.rb +58 -0
  114. data/test/acceptance/updating_props_test.rb +72 -0
  115. data/test/support/config.rb +1 -0
  116. data/test/unit/extensions/events_extension_test.rb +33 -0
  117. data/test/unit/extensions/layout_extension_test.rb +74 -0
  118. data/test/unit/extensions/navigation_extension_test.rb +62 -0
  119. data/test/unit/extensions/oauth_extension_test.rb +91 -0
  120. data/test/unit/extensions/project_features_extension_test.rb +79 -0
  121. data/test/unit/extensions/serializers_extension_test.rb +47 -0
  122. data/test/unit/extensions/view_extension_test.rb +98 -0
  123. data/test/unit/models/actions_test.rb +11 -5
  124. data/test/unit/models/configuration_test.rb +0 -8
  125. data/test/unit/models/observer_test.rb +16 -0
  126. data/test/unit/models/persistent_trigger_test.rb +29 -2
  127. data/test/unit/models/serializer_test.rb +6 -0
  128. data/test/unit/models/timer_test.rb +88 -0
  129. metadata +87 -168
  130. data/app/assets/font/octicons.eot +0 -0
  131. data/app/assets/font/octicons.svg +0 -198
  132. data/app/assets/font/octicons.ttf +0 -0
  133. data/app/assets/font/octicons.woff +0 -0
  134. data/app/assets/font/roboto-black-webfont.eot +0 -0
  135. data/app/assets/font/roboto-black-webfont.svg +0 -675
  136. data/app/assets/font/roboto-black-webfont.ttf +0 -0
  137. data/app/assets/font/roboto-black-webfont.woff +0 -0
  138. data/app/assets/font/roboto-blackitalic-webfont.eot +0 -0
  139. data/app/assets/font/roboto-blackitalic-webfont.svg +0 -677
  140. data/app/assets/font/roboto-blackitalic-webfont.ttf +0 -0
  141. data/app/assets/font/roboto-blackitalic-webfont.woff +0 -0
  142. data/app/assets/font/roboto-bold-webfont.eot +0 -0
  143. data/app/assets/font/roboto-bold-webfont.svg +0 -675
  144. data/app/assets/font/roboto-bold-webfont.ttf +0 -0
  145. data/app/assets/font/roboto-bold-webfont.woff +0 -0
  146. data/app/assets/font/roboto-bolditalic-webfont.eot +0 -0
  147. data/app/assets/font/roboto-bolditalic-webfont.svg +0 -677
  148. data/app/assets/font/roboto-bolditalic-webfont.ttf +0 -0
  149. data/app/assets/font/roboto-bolditalic-webfont.woff +0 -0
  150. data/app/assets/font/roboto-italic-webfont.eot +0 -0
  151. data/app/assets/font/roboto-italic-webfont.svg +0 -668
  152. data/app/assets/font/roboto-italic-webfont.ttf +0 -0
  153. data/app/assets/font/roboto-italic-webfont.woff +0 -0
  154. data/app/assets/font/roboto-light-webfont.eot +0 -0
  155. data/app/assets/font/roboto-light-webfont.svg +0 -666
  156. data/app/assets/font/roboto-light-webfont.ttf +0 -0
  157. data/app/assets/font/roboto-light-webfont.woff +0 -0
  158. data/app/assets/font/roboto-lightitalic-webfont.eot +0 -0
  159. data/app/assets/font/roboto-lightitalic-webfont.svg +0 -668
  160. data/app/assets/font/roboto-lightitalic-webfont.ttf +0 -0
  161. data/app/assets/font/roboto-lightitalic-webfont.woff +0 -0
  162. data/app/assets/font/roboto-medium-webfont.eot +0 -0
  163. data/app/assets/font/roboto-medium-webfont.svg +0 -675
  164. data/app/assets/font/roboto-medium-webfont.ttf +0 -0
  165. data/app/assets/font/roboto-medium-webfont.woff +0 -0
  166. data/app/assets/font/roboto-mediumitalic-webfont.eot +0 -0
  167. data/app/assets/font/roboto-mediumitalic-webfont.svg +0 -677
  168. data/app/assets/font/roboto-mediumitalic-webfont.ttf +0 -0
  169. data/app/assets/font/roboto-mediumitalic-webfont.woff +0 -0
  170. data/app/assets/font/roboto-regular-webfont.eot +0 -0
  171. data/app/assets/font/roboto-regular-webfont.svg +0 -666
  172. data/app/assets/font/roboto-regular-webfont.ttf +0 -0
  173. data/app/assets/font/roboto-regular-webfont.woff +0 -0
  174. data/app/assets/font/roboto-thin-webfont.eot +0 -0
  175. data/app/assets/font/roboto-thin-webfont.svg +0 -666
  176. data/app/assets/font/roboto-thin-webfont.ttf +0 -0
  177. data/app/assets/font/roboto-thin-webfont.woff +0 -0
  178. data/app/assets/font/roboto-thinitalic-webfont.eot +0 -0
  179. data/app/assets/font/roboto-thinitalic-webfont.svg +0 -668
  180. data/app/assets/font/roboto-thinitalic-webfont.ttf +0 -0
  181. data/app/assets/font/roboto-thinitalic-webfont.woff +0 -0
  182. data/app/assets/images/bug-fixed-128.png +0 -0
  183. data/app/assets/images/bug-fixed-32.png +0 -0
  184. data/app/assets/images/bug-fixed-48.png +0 -0
  185. data/app/assets/images/bug-new-128.png +0 -0
  186. data/app/assets/images/bug-new-32.png +0 -0
  187. data/app/assets/images/bug-new-48.png +0 -0
  188. data/app/assets/images/bug-open-32.png +0 -0
  189. data/app/assets/images/bug-zero-128.png +0 -0
  190. data/app/assets/images/bug-zero-48.png +0 -0
  191. data/app/assets/images/drag-grip.png +0 -0
  192. data/app/assets/javascripts/houston/core/burndown_chart.coffee +0 -111
  193. data/app/assets/javascripts/houston/core/stacked_area_graph.coffee +0 -113
  194. data/app/assets/javascripts/houston/core/stacked_bar_graph.coffee +0 -108
  195. data/app/assets/stylesheets/houston/application/project_tiles.scss +0 -26
  196. data/app/assets/stylesheets/houston/application/tips.scss +0 -5
  197. data/app/assets/stylesheets/houston/core/octicons-icons.scss +0 -221
  198. data/app/assets/stylesheets/houston/core/octicons.scss.erb +0 -9
  199. data/app/assets/stylesheets/houston/core/roboto.scss.erb +0 -131
  200. data/app/concerns/historical_weekly_stats.rb +0 -15
  201. data/app/concerns/nosync.rb +0 -21
  202. data/app/controllers/oauth/providers_controller.rb +0 -45
  203. data/app/controllers/project_roles_controller.rb +0 -22
  204. data/app/controllers/settings_controller.rb +0 -14
  205. data/app/controllers/tester_bar_controller.rb +0 -12
  206. data/app/controllers/user_credentials_controller.rb +0 -24
  207. data/app/models/role.rb +0 -33
  208. data/app/models/setting.rb +0 -10
  209. data/app/models/settings.rb +0 -38
  210. data/app/models/slackdown.rb +0 -23
  211. data/app/models/user_credentials.rb +0 -27
  212. data/app/views/errors/_actions.html.erb +0 -17
  213. data/app/views/layouts/_tester_bar.html.erb +0 -6
  214. data/app/views/layouts/minimal.html.erb +0 -50
  215. data/app/views/layouts/naked.html.erb +0 -47
  216. data/app/views/layouts/naked_dashboard.html.erb +0 -50
  217. data/app/views/oauth/providers/_form.html.erb +0 -54
  218. data/app/views/oauth/providers/index.html.erb +0 -41
  219. data/app/views/oauth/providers/new.html.erb +0 -7
  220. data/config/initializers/add_navigation_renderers.rb +0 -5
  221. data/config/initializers/vestal_versions.rb +0 -9
  222. data/db/migrate/20130519163615_create_user_credentials.rb +0 -18
  223. data/lib/houston/boot/events.rb +0 -10
  224. data/lib/tasks/keypair.rake +0 -17
  225. data/vendor/assets/javascripts/jquery.pjax.js +0 -817
@@ -0,0 +1,194 @@
1
+ module Houston
2
+ module Extensions
3
+ module Deprecated
4
+
5
+
6
+ def add_project_column(_slug, &block)
7
+ Houston.deprecation_notice 'Houston.add_project_column is deprecated and will be removed in houston-core 1.0; use Houston.views["project"].add_column instead'
8
+
9
+ dsl = DeprecatedColumnDsl.new
10
+ dsl.instance_eval(&block)
11
+ dsl.add_to Houston.view["projects"]
12
+ end
13
+
14
+ def add_user_option(_slug, &block)
15
+ Houston.deprecation_notice 'Houston.add_user_option is deprecated and will be removed in houston-core 1.0; use Houston.views["edit_user"].add_field instead'
16
+
17
+ dsl = DeprecatedFieldDsl.new
18
+ dsl.instance_eval(&block)
19
+ dsl.add_to Houston.view["edit_user"]
20
+ end
21
+
22
+ def add_project_option(_slug, &block)
23
+ Houston.deprecation_notice 'Houston.add_project_option is deprecated and will be removed in houston-core 1.0; use Houston.views["edit_project"].add_field instead'
24
+
25
+ dsl = DeprecatedFieldDsl.new
26
+ dsl.instance_eval(&block)
27
+ dsl.add_to Houston.view["edit_project"]
28
+ end
29
+
30
+ def add_navigation_renderer(slug, &block)
31
+ Houston.deprecation_notice 'Houston.add_navigation_renderer is deprecated and will be removed in houston-core 1.0; use Houston.navigation.add_link instead'
32
+
33
+ dsl = DeprecatedNavigationDsl.new
34
+ dsl.instance_eval(&block)
35
+ dsl.add_to_navigation(slug)
36
+ end
37
+
38
+ def add_project_feature(slug, &block)
39
+ Houston.deprecation_notice 'Houston.add_project_feature is deprecated and will be removed in houston-core 1.0; use Houston.project_features.add instead'
40
+
41
+ dsl = DeprecatedProjectFeatureDsl.new
42
+ dsl.instance_eval(&block)
43
+ dsl.add_to_project_features(slug)
44
+ end
45
+
46
+ def add_project_header_command(slug, &block)
47
+ Houston.deprecation_notice 'Houston.add_project_header_command is deprecated and will be removed in houston-core 1.0'
48
+
49
+ dsl = ProjectBannerFeatureDsl.new(ProjectBannerFeature.new)
50
+ dsl.instance_eval(&block)
51
+ feature = dsl.feature
52
+ feature.slug = slug
53
+
54
+ project_header_commands.push feature
55
+ end
56
+
57
+ def project_header_commands
58
+ @project_header_commands ||= []
59
+ end
60
+
61
+
62
+
63
+
64
+
65
+
66
+ class DeprecatedColumnDsl
67
+ def name(value)
68
+ @name = value
69
+ end
70
+
71
+ def html(&block)
72
+ @render_block = block
73
+ end
74
+
75
+ def ability(&block)
76
+ @ability_block = block
77
+ end
78
+
79
+ def add_to(view)
80
+ column = view.add_column @name, &@render_block
81
+ ability_block = @ability_block
82
+ column.ability { ability_block.call(self) } if ability_block
83
+ column
84
+ end
85
+ end
86
+
87
+ class DeprecatedFieldDsl
88
+ attr_reader :label, :render_block
89
+
90
+ def name(value)
91
+ @label = value
92
+ end
93
+
94
+ def html(&block)
95
+ @render_block = block
96
+ end
97
+
98
+ def add_to(view)
99
+ render_block = @render_block
100
+ view.add_field(@label) { |*args| instance_exec(*args, &render_block).html_safe }
101
+ end
102
+ end
103
+
104
+ class DeprecatedNavigationDsl
105
+ def name(value)
106
+ @name = value
107
+ end
108
+
109
+ def path(&block)
110
+ @path_block = block
111
+ end
112
+
113
+ def ability(&block)
114
+ @ability_block = block
115
+ end
116
+
117
+ def add_to_navigation(slug)
118
+ Houston.navigation.add_link(slug, &@path_block).tap do |link|
119
+ ability_block = @ability_block
120
+ link.ability { ability_block.call(self) } if ability_block
121
+ link.name { @name } unless @name == slug.to_s.titleize
122
+ end
123
+ end
124
+ end
125
+
126
+ class DeprecatedProjectFeatureDsl
127
+ def initialize
128
+ @field_blocks = []
129
+ end
130
+
131
+ def name(value)
132
+ @name = value
133
+ end
134
+
135
+ def path(&block)
136
+ @path_block = block
137
+ end
138
+
139
+ def ability(&block)
140
+ @ability_block = block
141
+ end
142
+
143
+ def field(_slug, &block)
144
+ @field_blocks.push block
145
+ end
146
+
147
+ def add_to_project_features(slug)
148
+ raise ArgumentError, "Project Feature must supply name, but #{slug.inspect} doesn't" unless @name
149
+ raise ArgumentError, "Project Feature must supply path lambda, but #{slug.inspect} doesn't" unless @path_block
150
+
151
+ Houston.project_features.add(slug, &@path_block).tap do |feature|
152
+ ability_block = @ability_block
153
+ feature.ability { |project| ability_block.call(self, project) } if ability_block
154
+ feature.name { @name } unless @name == slug.to_s.titleize
155
+
156
+ @field_blocks.each do |block|
157
+ dsl = DeprecatedFieldDsl.new
158
+ dsl.instance_eval(&block)
159
+ dsl.add_to feature
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ class ProjectBannerFeature
166
+ attr_accessor :partial
167
+ attr_accessor :ability_block
168
+ attr_accessor :slug
169
+
170
+ def permitted?(ability, project)
171
+ return true if ability_block.nil?
172
+ ability_block.call ability, project
173
+ end
174
+ end
175
+
176
+ class ProjectBannerFeatureDsl
177
+ attr_reader :feature
178
+
179
+ def initialize(feature)
180
+ @feature = feature
181
+ end
182
+
183
+ def partial(value)
184
+ feature.partial = value
185
+ end
186
+
187
+ def ability(&block)
188
+ feature.ability_block = block
189
+ end
190
+ end
191
+
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,99 @@
1
+ require "delegate"
2
+
3
+ module Houston
4
+ module Extensions
5
+ module Permitted
6
+ def permitted?(ability, *args)
7
+ return true if @ability_block.nil?
8
+ ability.instance_exec(*args, &@ability_block)
9
+ end
10
+ end
11
+
12
+ module Render
13
+ def render(view, *args)
14
+ view.instance_exec(*args, &@render_block)
15
+ end
16
+ end
17
+
18
+ module LinkTo
19
+ def path(*args)
20
+ @path_block.call(*args)
21
+ end
22
+ end
23
+
24
+ module AbilityBlock
25
+ def ability(&block)
26
+ __getobj__.instance_variable_set :@ability_block, block
27
+ end
28
+ end
29
+
30
+ module AcceptsName
31
+ def name(*args)
32
+ __getobj__.name = args.first if args.any?
33
+ __getobj__.name
34
+ end
35
+ end
36
+
37
+ module HasTable
38
+ attr_reader :columns
39
+
40
+ def self.extended(view)
41
+ view.instance_variable_set :@columns, []
42
+ end
43
+
44
+ def add_column(name, &block)
45
+ Chain(AbilityBlock, Column.new(name).tap do |column|
46
+ column.instance_variable_set :@render_block, block
47
+ @columns << column
48
+ end)
49
+ end
50
+
51
+ def reset!
52
+ @columns = []
53
+ super
54
+ end
55
+
56
+ Column = Struct.new(:name) do
57
+ include Permitted, Render
58
+ end
59
+ end
60
+
61
+ module HasForm
62
+ attr_reader :fields
63
+
64
+ def self.extended(view)
65
+ view.instance_variable_set :@fields, []
66
+ end
67
+
68
+ def add_field(label, &block)
69
+ Chain(AbilityBlock, Field.new(label).tap do |field|
70
+ field.instance_variable_set :@render_block, block
71
+ @fields << field
72
+ end)
73
+ end
74
+
75
+ def reset!
76
+ @fields = []
77
+ super
78
+ end
79
+
80
+ Field = Struct.new(:label) do
81
+ include Permitted, Render
82
+
83
+ def id
84
+ "__props_#{label.tr(" ", "_").underscore}"
85
+ end
86
+ end
87
+ end
88
+
89
+ end
90
+ end
91
+
92
+ def Chain(*modules)
93
+ object = modules.pop
94
+ Class.new(SimpleDelegator).new(object).tap do |builder|
95
+ modules.each do |mod|
96
+ builder.extend mod
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,81 @@
1
+ require "concurrent/array"
2
+
3
+ module Houston
4
+ module Extensions
5
+ class Events
6
+ include Enumerable
7
+
8
+ def initialize
9
+ @events = Concurrent::Array.new
10
+ end
11
+
12
+ def each(&block)
13
+ @events.each(&block)
14
+ end
15
+
16
+ def [](event_name)
17
+ @events.find { |event| event.matches? event_name }
18
+ end
19
+
20
+ def registered?(event_name)
21
+ @events.any? { |event| event.matches? event_name }
22
+ end
23
+
24
+ def register(&block)
25
+ dsl = RegisterEventsDsl.new
26
+ hash = dsl.instance_eval(&block)
27
+ hash.each do |name, description|
28
+ @events.push Event.new(name, description.to_h)
29
+ end
30
+ end
31
+ end
32
+
33
+
34
+ class RegisterEventsDsl
35
+ def params(*params)
36
+ RegisterEventDsl.new.params(*params)
37
+ end
38
+
39
+ def description(value)
40
+ RegisterEventDsl.new.description(value)
41
+ end
42
+ alias :desc :description
43
+ end
44
+
45
+
46
+ class RegisterEventDsl
47
+ def initialize
48
+ @hash = {}
49
+ end
50
+
51
+ def params(*params)
52
+ @hash[:params] = params
53
+ self
54
+ end
55
+
56
+ def description(value)
57
+ @hash[:description] = value
58
+ self
59
+ end
60
+ alias :desc :description
61
+
62
+ def to_h
63
+ @hash
64
+ end
65
+ end
66
+
67
+
68
+ Event = Struct.new(:name, :description, :params) do
69
+ def initialize(name, options)
70
+ super name,
71
+ options.fetch(:description),
72
+ options.fetch(:params, [])
73
+ @matcher = Regexp.new("\\A#{name.gsub /\{([^:}]+)\}/, "(?<\\1>[^:]+)"}\\z")
74
+ end
75
+
76
+ def matches?(event_name)
77
+ @matcher === event_name # TODO: replace `===` with `.match?` on Ruby 2.4
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,42 @@
1
+ require "concurrent/array"
2
+ require "delegate"
3
+ require "houston/boot/extensions/dsl"
4
+
5
+ module Houston
6
+ module Extensions
7
+ class Features
8
+
9
+ def initialize
10
+ @features = Concurrent::Hash.new
11
+ end
12
+
13
+ def add(content, &path_block)
14
+ if content.is_a?(Symbol)
15
+ slug = content
16
+ content = slug.to_s.titleize
17
+ else
18
+ slug = content.underscore.to_sym
19
+ end
20
+ Chain(AbilityBlock, AcceptsName, Feature.new(slug, content).tap do |feature|
21
+ feature.instance_variable_set :@path_block, path_block
22
+ feature.extend Houston::Extensions::HasForm
23
+ @features[slug] = feature
24
+ end)
25
+ end
26
+ alias :<< :add
27
+
28
+ def all
29
+ @features.keys
30
+ end
31
+ alias :to_a :all
32
+
33
+ def [](slug)
34
+ @features.fetch(slug)
35
+ end
36
+ end
37
+
38
+ Feature = Struct.new(:slug, :name) do
39
+ include Permitted, LinkTo
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,70 @@
1
+ module Houston
2
+ module Extensions
3
+ class Layout
4
+ attr_reader :extensions_by_layout
5
+
6
+ def initialize
7
+ reset!
8
+ end
9
+
10
+ def reset!
11
+ @extensions_by_layout =
12
+ { "application" => Extensions.new,
13
+ "dashboard" => Extensions.new }
14
+ end
15
+
16
+ def [](layout)
17
+ raise ArgumentError, "#{layout} is not a layout. Valid layouts are #{extensions_by_layout.keys.join(", ")}" unless extensions_by_layout.key?(layout)
18
+ ExtensionDsl.new(extensions_by_layout, layout)
19
+ end
20
+
21
+ def all
22
+ ExtensionDsl.new(extensions_by_layout, *extensions_by_layout.keys)
23
+ end
24
+
25
+ delegate :meta, :stylesheets, :footers, :scripts, to: :all
26
+
27
+
28
+ class ExtensionDsl
29
+
30
+ def initialize(registry, *layouts)
31
+ @registry = registry
32
+ @layouts = layouts
33
+ end
34
+
35
+ def meta(&block)
36
+ add :meta, block
37
+ end
38
+
39
+ def stylesheets(&block)
40
+ add :stylesheets, block
41
+ end
42
+
43
+ def footers(&block)
44
+ add :footers, block
45
+ end
46
+
47
+ def scripts(&block)
48
+ add :scripts, block
49
+ end
50
+
51
+ private
52
+ attr_reader :registry, :layouts
53
+
54
+ def add(method, block)
55
+ layouts.each do |layout|
56
+ registry[layout].public_send(method) << block
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ Extensions = Struct.new(:meta, :stylesheets, :footers, :scripts) do
63
+ def initialize
64
+ super [], [], [], []
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end