conjoin 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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