conjoin 0.0.1 → 0.0.2

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.
@@ -0,0 +1,4 @@
1
+ dir = Gem::Specification.find_by_name('cuba-tracks').gem_dir
2
+
3
+ Dir.glob("#{dir}/lib/cuba/tasks/**/*.rb").each { |r| require r }
4
+ Dir.glob("#{dir}/lib/cuba/tasks/**/*.rake").each { |r| import r }
@@ -0,0 +1,70 @@
1
+ namespace :db do
2
+ desc "create the database from config/database.yml from the current Sinatra env"
3
+ task :create do
4
+ ActiveRecordTasks.create()
5
+ end
6
+
7
+ desc "drops the data from config/database.yml from the current Sinatra env"
8
+ task :drop do
9
+ ActiveRecordTasks.drop()
10
+ end
11
+
12
+ desc "load the seed data from db/seeds.rb or run specific seed rake db:seed[file/path]"
13
+ task :seed, :file do |t, args|
14
+ ActiveRecordTasks.seed args[:file]
15
+ end
16
+
17
+ desc "create the database and load the schema"
18
+ task :setup do
19
+ ActiveRecordTasks.setup()
20
+ end
21
+
22
+ desc "create an ActiveRecord migration"
23
+ task :create_migration do
24
+ ActiveRecordTasks.create_migration(ENV["NAME"], ENV["VERSION"])
25
+ end
26
+
27
+ desc "migrate the database (use version with VERSION=n)"
28
+ task :migrate do
29
+ ActiveRecordTasks.migrate(ENV["VERSION"])
30
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
31
+ end
32
+
33
+ desc "roll back the migration (use steps with STEP=n)"
34
+ task :rollback do
35
+ ActiveRecordTasks.rollback(ENV["STEP"])
36
+ Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
37
+ end
38
+
39
+ namespace :schema do
40
+ desc "dump schema into file"
41
+ task :dump do
42
+ ActiveRecordTasks.dump_schema()
43
+ end
44
+
45
+ desc "load schema into database"
46
+ task :load do
47
+ ActiveRecordTasks.load_schema()
48
+ end
49
+ end
50
+
51
+ namespace :test do
52
+ task :purge do
53
+ ActiveRecordTasks.with_config_environment 'test' do
54
+ ActiveRecordTasks.purge()
55
+ end
56
+ end
57
+
58
+ task :load => :purge do
59
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
60
+ ActiveRecordTasks.with_config_environment 'test' do
61
+ ActiveRecordTasks.load_schema()
62
+ end
63
+ end
64
+
65
+ desc 'Prepare test database from development schema'
66
+ task :prepare do
67
+ Rake::Task["db:test:load"].invoke
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,142 @@
1
+ require 'active_record'
2
+ require 'active_support/core_ext/string/strip'
3
+ require 'fileutils'
4
+ require 'highline/import'
5
+
6
+ module ActiveRecordTasks
7
+ extend self
8
+
9
+ def create
10
+ silence_activerecord do
11
+ ActiveRecord::Tasks::DatabaseTasks.create(config)
12
+ end
13
+ end
14
+
15
+ def drop
16
+ silence_activerecord do
17
+ ActiveRecord::Tasks::DatabaseTasks.drop(config)
18
+ end
19
+ end
20
+
21
+ def seed file = false
22
+ if ENV['RACK_ENV'] == 'production'
23
+ continue = ask("Running seeds could override data, proceed y/N? ", String)
24
+ else
25
+ continue = 'y'
26
+ end
27
+
28
+ if %w(yes y Y).include? continue
29
+ silence_activerecord do
30
+ load("db/seeds.rb")
31
+ if not file
32
+ Seeds.run
33
+ else
34
+ load("db/seeds/#{file}.rb")
35
+ end
36
+ end
37
+ else
38
+ say 'Aborting!'
39
+ end
40
+ end
41
+
42
+ def setup
43
+ silence_activerecord do
44
+ create()
45
+ load_schema()
46
+ seed()
47
+ end
48
+ end
49
+
50
+ def create_migration(migration_name, version = nil)
51
+ raise "No NAME specified. Example usage: `rake db:create_migration NAME=create_users`" if migration_name.nil?
52
+
53
+ migration_number = version || Time.now.utc.strftime("%Y%m%d%H%M%S")
54
+ migration_file = File.join(migrations_dir, "#{migration_number}_#{migration_name}.rb")
55
+ migration_class = migration_name.split("_").map(&:capitalize).join
56
+
57
+ FileUtils.mkdir_p(migrations_dir)
58
+ File.open(migration_file, 'w') do |file|
59
+ file.write <<-MIGRATION.strip_heredoc
60
+ class #{migration_class} < ActiveRecord::Migration
61
+ def change
62
+ end
63
+ end
64
+ MIGRATION
65
+ end
66
+ end
67
+
68
+ def migrate(version = nil)
69
+ silence_activerecord do
70
+ migration_version = version ? version.to_i : version
71
+ ActiveRecord::Migrator.migrate(migrations_dir, migration_version)
72
+ end
73
+ end
74
+
75
+ def rollback(step = nil)
76
+ silence_activerecord do
77
+ migration_step = step ? step.to_i : 1
78
+ ActiveRecord::Migrator.rollback(migrations_dir, migration_step)
79
+ end
80
+ end
81
+
82
+ def dump_schema(file_name = 'db/schema.rb')
83
+ silence_activerecord do
84
+ ActiveRecord::Migration.suppress_messages do
85
+ # Create file
86
+ out = File.new(file_name, 'w')
87
+
88
+ # Load schema
89
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, out)
90
+
91
+ out.close
92
+ end
93
+ end
94
+ end
95
+
96
+ def purge
97
+ if config
98
+ ActiveRecord::Tasks::DatabaseTasks.purge(config)
99
+ else
100
+ raise ActiveRecord::ConnectionNotEstablished
101
+ end
102
+ end
103
+
104
+ def load_schema(file_name = 'db/schema.rb')
105
+ load(file_name)
106
+ end
107
+
108
+ private
109
+
110
+ def config
111
+ db = URI.parse ENV['DATABASE_URL']
112
+
113
+ {
114
+ adapter: db.scheme == 'postgres' ? 'postgresql' : db.scheme,
115
+ encoding: 'utf8',
116
+ reconnect: true,
117
+ database: db.path[1..-1],
118
+ host: db.host,
119
+ port: db.port,
120
+ pool: ENV['DATABASE_POOL'] || 5,
121
+ username: db.user,
122
+ password: db.password
123
+ }
124
+ end
125
+
126
+ def connect_to_active_record
127
+ ActiveRecord::Base.establish_connection config
128
+ end
129
+
130
+ def migrations_dir
131
+ ActiveRecord::Migrator.migrations_path
132
+ end
133
+
134
+ def silence_activerecord(&block)
135
+ connect_to_active_record
136
+
137
+ old_logger = ActiveRecord::Base.logger
138
+ ActiveRecord::Base.logger = nil
139
+ yield if block_given?
140
+ ActiveRecord::Base.logger = old_logger
141
+ end
142
+ end
@@ -1,3 +1,3 @@
1
1
  module Conjoin
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,323 @@
1
+ require "observer"
2
+
3
+ module Conjoin
4
+ module Widgets
5
+ class << self
6
+ attr_accessor :app
7
+ end
8
+
9
+ def self.setup app
10
+ self.app = app
11
+ app.settings[:widgets_root] = "#{app.root}/app/widgets"
12
+ app.settings[:widgets] ||= {}
13
+
14
+ Dir["#{app.root}/app/widgets/**/*.rb"].each { |rb| require rb }
15
+ end
16
+
17
+ def widgets list = false
18
+ widget_name, incoming_event, event = load_widgets
19
+
20
+ if incoming_event
21
+ res.headers["Content-Type"] = "text/javascript; charset=utf-8"
22
+ event.trigger incoming_event.to_sym, widget_name, req.params
23
+ res.write "$('head > meta[name=csrf-token]').attr('content', '#{csrf_token}');"
24
+ res.write '$(document).trigger("page:change");'
25
+ end
26
+
27
+ true
28
+ end
29
+
30
+ def widgets_root
31
+ settings[:widgets_root]
32
+ end
33
+
34
+ def render_widget *args
35
+ load_widgets unless req.env[:loaded_widgets]
36
+
37
+ if args.first.kind_of? Hash
38
+ opts = args.first
39
+ name = req.env[:widget_name]
40
+ else
41
+ name = args.first
42
+ opts = args.length > 1 ? args.last : {}
43
+ end
44
+
45
+ # set the current state (the method that will get called on render_widget)
46
+ state = opts[:state] || 'display'
47
+
48
+ widget = req.env[:loaded_widgets][name]
49
+
50
+ if widget.method(state).parameters.length > 0
51
+ widget.send state, opts
52
+ else
53
+ widget.send state
54
+ end
55
+ end
56
+ alias_method :widget_render, :render_widget
57
+
58
+ def widget_div opts = {}, &block
59
+ defaults = {
60
+ id: "#{req.env[:widget_name]}_#{req.env[:widget_state]}"
61
+ }.merge opts
62
+
63
+ name = req.env[:widget_name]
64
+ widget = req.env[:loaded_widgets][name]
65
+
66
+ html = block.call(widget)
67
+
68
+ mab do
69
+ div defaults do
70
+ text! html
71
+ end
72
+ end
73
+ end
74
+
75
+ def url_for_event event, options = {}
76
+ widget_name = req.env[:widget_name]
77
+ "#{path_for('widgets')}?widget_event=#{event}&widget_name=#{widget_name}&" + URI.encode_www_form(options)
78
+ end
79
+
80
+ def load_widgets
81
+ req.env[:loaded_widgets] ||= {}
82
+
83
+ event = Events.new res, req, settings
84
+
85
+ if incoming_event = req.params["widget_event"]
86
+ widget_name = req.params["widget_name"]
87
+ opts = { from_event: true }
88
+ else
89
+ opts = {}
90
+ end
91
+
92
+ settings[:widgets].each do |name, widget|
93
+ req.env[:loaded_widgets][name] = widget.constantize.new(self, res, req, settings, event, name, opts)
94
+ end
95
+
96
+ [widget_name, incoming_event, event]
97
+ end
98
+
99
+ module ClassMethods
100
+ def has_widgets *list
101
+ list.each do |widget|
102
+ settings[:widgets].merge! widget
103
+ end
104
+ end
105
+
106
+ def widgets_root= path
107
+ settings[:widgets_root] = path
108
+ end
109
+
110
+ def widgets_root
111
+ settings[:widgets_root]
112
+ end
113
+ end
114
+
115
+ class Events < Struct.new(:res, :req, :settings)
116
+ include Observable
117
+
118
+ def trigger event, widget_name, user_data = {}
119
+ # data = OpenStruct.new({
120
+ # settings: settings,
121
+ # data: user_data
122
+ # })
123
+ data = user_data.to_ostruct
124
+
125
+ # THIS IS WHAT WILL MAKE SURE EVENTS ARE TRIGGERED
126
+ changed
127
+ ##################################################
128
+
129
+ notify_observers event, widget_name, data
130
+ end
131
+ end
132
+
133
+ class Base
134
+ JS_ESCAPE = { '\\' => '\\\\', '</' => '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'" }
135
+ attr_accessor :app, :res, :req, :settings, :event, :folder, :options
136
+
137
+ def initialize app, res, req, settings, event, folder, options
138
+ @app = app
139
+ @res = res
140
+ @req = req
141
+ @settings = settings
142
+ @event = event
143
+ @folder = folder
144
+ @options = options
145
+
146
+ # add the widget to the req widgets
147
+ req.env[:widgets] ||= {}
148
+ unless req.env[:widgets][folder]
149
+ req.env[:widgets][folder] = {}
150
+ end
151
+
152
+ event.add_observer self, :trigger_event
153
+ end
154
+
155
+ def current_user
156
+ app.current_user
157
+ end
158
+
159
+ def id_for state
160
+ "#{req.env[:widget_name]}_#{state}"
161
+ end
162
+
163
+ def page_change
164
+ res.headers["Content-Type"] = "text/javascript; charset=utf-8"
165
+ res.write '$(document).trigger("page:change");'
166
+ end
167
+
168
+ def replace state, opts = {}
169
+ @options[:replace] = true
170
+
171
+ if !state.is_a? String
172
+ content = render state, opts
173
+ selector = '#' + id_for(state)
174
+ else
175
+ if !opts.key?(:content) and !opts.key?(:with)
176
+ content = render opts
177
+ else
178
+ content = opts[:content] || opts[:with]
179
+ end
180
+ selector = state
181
+ end
182
+
183
+ res.write '$("' + selector + '").replaceWith("' + escape(content) + '");'
184
+ # scroll to the top of the page just as if we went to the url directly
185
+ res.write 'window.scrollTo(0, 0);'
186
+ end
187
+
188
+ def hide selector
189
+ res.write '$("' + selector + '").hide();'
190
+ end
191
+
192
+ def append selector, opts = {}
193
+ content = render opts
194
+ res.write '$("' + selector + '").append("'+ escape(content) +'");'
195
+ end
196
+
197
+ def add_after selector, opts = {}
198
+ content = render opts
199
+ res.write '$("' + selector + '").after("'+ escape(content) +'");'
200
+ end
201
+
202
+ def attrs_for selector, opts = {}
203
+ res.write '$("' + selector + '").attr('+ (opts.to_json) +');'
204
+ end
205
+
206
+ def escape js
207
+ js.to_s.gsub(/(\\|<\/|\r\n|\\3342\\2200\\2250|[\n\r"'])/) {|match| JS_ESCAPE[match] }
208
+ end
209
+
210
+ def trigger_event event, widget_name, data = {}
211
+ if events = self.class.events
212
+ events.each do |class_event, opts|
213
+ if class_event == event && (widget_name == folder.to_s or opts[:for].to_s == widget_name)
214
+ event = opts[:with] if opts[:with]
215
+
216
+ if method(event).parameters.length > 0
217
+ send(event, data)
218
+ else
219
+ send(event)
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ def render *args
227
+ if args.first.kind_of? Hash
228
+ locals = args.first
229
+ # if it's a partial we add an underscore infront of it
230
+ state = view = locals[:state] || "#{locals[:partial]}".gsub(/([a-zA-Z_]+)$/, '_\1')
231
+ else
232
+ state = view = args.first
233
+ locals = args.length > 1 ? args.last : {}
234
+ end
235
+
236
+ unless view
237
+ state = view = caller[0][/`.*'/][1..-2]
238
+
239
+ if (options.key?(:from_event) and !options.key?(:replace))
240
+ @options[:cache] = false
241
+ end
242
+ end
243
+
244
+ if locals.key?(:state) and state.to_s == view
245
+ return send state
246
+ end
247
+
248
+ tmpl_engine = settings[:render][:template_engine]
249
+
250
+ if (req_helper_methods = req.env[:widgets][folder][:req_helper_methods]) \
251
+ and (!options.key?(:cache))
252
+ locals.merge! req_helper_methods
253
+ else
254
+ req.env[:widgets][folder][:req_helper_methods] = {}
255
+
256
+ helper_methods.each do |method|
257
+ req.env[:widgets][folder][:req_helper_methods][method] = locals[method] = self.send method
258
+ end
259
+ end
260
+
261
+ req.env[:widget_name] = folder
262
+ req.env[:widget_state] = state
263
+
264
+ locals[:w] = locals[:widget] = self
265
+
266
+ app.render "#{app.widgets_root}/#{folder}/#{view}.#{tmpl_engine}", locals
267
+ end
268
+
269
+ private
270
+
271
+ def helper_methods
272
+ self.class.available_helper_methods || []
273
+ end
274
+
275
+ class << self
276
+ attr_accessor :events, :available_helper_methods
277
+
278
+ def respond_to event, opts = {}
279
+ @events ||= []
280
+ @events << [event, opts]
281
+ end
282
+
283
+ def responds_to *events
284
+ @events ||= []
285
+ events.each do |event|
286
+ @events << [event, {}]
287
+ end
288
+ end
289
+
290
+ def helper_method method
291
+ @available_helper_methods ||= []
292
+ @available_helper_methods << method
293
+ end
294
+
295
+ def helper_methods *methods
296
+ methods.each do |method|
297
+ helper_method method
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ class Routes < Struct.new(:settings)
304
+ def app
305
+ App.settings = settings
306
+ App.plugin Conjoin::Cuba::Render
307
+ App.plugin Conjoin::Auth
308
+ App.plugin Conjoin::Assets
309
+ App.plugin Conjoin::I18N
310
+ App.plugin Conjoin::FormBuilder
311
+ App.plugin Conjoin::Widgets
312
+
313
+ App
314
+ end
315
+ end
316
+
317
+ class App < Conjoin::Cuba
318
+ define do
319
+ on(default, widgets) {}
320
+ end
321
+ end
322
+ end
323
+ end