hobo 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/hobo_files/plugin/CHANGES.txt +220 -23
  2. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +18 -25
  3. data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +20 -15
  4. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +3 -3
  5. data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +3 -3
  6. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.css +1 -2
  7. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +21 -4
  8. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/images/fieldbg.gif +0 -0
  9. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/images/spinner.gif +0 -0
  10. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +154 -26
  11. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +144 -0
  12. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +1 -1
  13. data/hobo_files/plugin/generators/hobo_user_controller/templates/controller.rb +1 -1
  14. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +8 -11
  15. data/hobo_files/plugin/init.rb +0 -2
  16. data/hobo_files/plugin/lib/active_record/has_many_association.rb +0 -9
  17. data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +0 -10
  18. data/hobo_files/plugin/lib/hobo.rb +57 -44
  19. data/hobo_files/plugin/lib/hobo/bundle.rb +222 -0
  20. data/hobo_files/plugin/lib/hobo/controller.rb +2 -5
  21. data/hobo_files/plugin/lib/hobo/dryml.rb +8 -7
  22. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +10 -21
  23. data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +107 -80
  24. data/hobo_files/plugin/lib/hobo/dryml/template.rb +27 -20
  25. data/hobo_files/plugin/lib/hobo/enum_string.rb +1 -1
  26. data/hobo_files/plugin/lib/hobo/field_declaration_dsl.rb +7 -0
  27. data/hobo_files/plugin/lib/hobo/guest.rb +4 -0
  28. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +37 -9
  29. data/hobo_files/plugin/lib/hobo/model.rb +79 -17
  30. data/hobo_files/plugin/lib/hobo/model_controller.rb +59 -60
  31. data/hobo_files/plugin/lib/hobo/model_router.rb +16 -4
  32. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +2 -1
  33. data/hobo_files/plugin/lib/hobo/user.rb +10 -7
  34. data/hobo_files/plugin/lib/hobo/user_controller.rb +6 -6
  35. data/hobo_files/plugin/{tags → taglibs}/core.dryml +5 -4
  36. data/hobo_files/plugin/{tags → taglibs}/rapid.dryml +54 -7
  37. data/hobo_files/plugin/{tags → taglibs}/rapid_document_tags.dryml +0 -0
  38. data/hobo_files/plugin/{tags → taglibs}/rapid_editing.dryml +4 -2
  39. data/hobo_files/plugin/{tags → taglibs}/rapid_forms.dryml +1 -4
  40. data/hobo_files/plugin/{tags → taglibs}/rapid_navigation.dryml +1 -2
  41. data/hobo_files/plugin/taglibs/rapid_pages.dryml +411 -0
  42. data/hobo_files/plugin/{tags → taglibs}/rapid_plus.dryml +0 -0
  43. data/hobo_files/plugin/{tags → taglibs}/rapid_support.dryml +9 -6
  44. data/hobo_files/plugin/tasks/fix_dryml.rake +0 -1
  45. metadata +16 -14
  46. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid_ui.css +0 -167
  47. data/hobo_files/plugin/lib/hobo/plugins.rb +0 -75
  48. data/hobo_files/plugin/tags/rapid_pages.dryml +0 -341
@@ -2,6 +2,10 @@ module Hobo
2
2
 
3
3
  module Model
4
4
 
5
+ NAME_FIELD_GUESS = %w(name title)
6
+ PRIMARY_CONTENT_GUESS = %w(description body content profile)
7
+ SEARCH_COLUMNS_GUESS = %w(name title body content profile)
8
+
5
9
  PLAIN_TYPES = { :boolean => TrueClass,
6
10
  :date => Date,
7
11
  :datetime => Time,
@@ -18,10 +22,12 @@ module Hobo
18
22
  base.extend(ClassMethods)
19
23
  base.class_eval do
20
24
  alias_method_chain :attributes=, :hobo_type_conversion
25
+ default_scopes
21
26
  end
22
27
  class << base
23
28
  alias_method_chain :has_many, :defined_scopes
24
29
  alias_method_chain :belongs_to, :foreign_key_declaration
30
+ alias_method_chain :belongs_to, :hobo_metadata
25
31
  alias_method_chain :acts_as_list, :fields if defined?(ActiveRecord::Acts::List)
26
32
  def inherited(klass)
27
33
  fields do
@@ -37,6 +43,44 @@ module Hobo
37
43
  # include methods also shared by CompositeModel
38
44
  include ModelSupport::ClassMethods
39
45
 
46
+ attr_accessor :creator_attribute
47
+ attr_writer :name_attribute, :primary_content_attribute
48
+
49
+ def default_scopes
50
+ def_scope :recent do |*args|
51
+ count = args.first || 3
52
+ { :limit => count, :order => "#{table_name}.created_at DESC" }
53
+ end
54
+ end
55
+
56
+ def name_attribute
57
+ @name_attribute ||= begin
58
+ cols = columns.every :name
59
+ NAME_FIELD_GUESS.detect {|f| f.in? columns.every(:name) }
60
+ end
61
+ end
62
+
63
+
64
+ def primary_content_attribute
65
+ @description_attribute ||= begin
66
+ cols = columns.every :name
67
+ PRIMARY_CONTENT_GUESS.detect {|f| f.in? columns.every(:name) }
68
+ end
69
+ end
70
+
71
+ def dependent_collections
72
+ reflections.values.select do |refl|
73
+ refl.macro == :has_many && refl.options[:dependent]
74
+ end.every(:name)
75
+ end
76
+
77
+
78
+ def dependent_on
79
+ reflections.values.select do |refl|
80
+ refl.macro == :belongs_to && (rev = reverse_reflection(refl.name) and rev.options[:dependent])
81
+ end.every(:name)
82
+ end
83
+
40
84
  private
41
85
 
42
86
  def return_type(type)
@@ -75,6 +119,13 @@ module Hobo
75
119
  end
76
120
  res
77
121
  end
122
+
123
+
124
+ def belongs_to_with_hobo_metadata(name, *args, &block)
125
+ options = args.extract_options!
126
+ self.creator_attribute = name.to_sym if options.delete(:creator)
127
+ belongs_to_without_hobo_metadata(name, *args + [options], &block)
128
+ end
78
129
 
79
130
 
80
131
  def acts_as_list_with_fields(options = {})
@@ -125,12 +176,6 @@ module Hobo
125
176
  end
126
177
  public :never_show?
127
178
 
128
- def set_creator_attr(attr)
129
- @creator_attr = attr.to_sym
130
- end
131
- attr_reader :creator_attr
132
- public :creator_attr
133
-
134
179
  def set_search_columns(*columns)
135
180
  class_eval %{
136
181
  def self.search_columns
@@ -281,12 +326,12 @@ module Hobo
281
326
  end
282
327
 
283
328
  def creator_type
284
- reflections[@creator_attr]._?.klass
329
+ reflections[creator_attribute]._?.klass
285
330
  end
286
331
 
287
332
  def search_columns
288
333
  cols = columns.every(:name)
289
- %w{name title body content}.select{|c| c.in?(cols) }
334
+ SEARCH_COLUMNS_GUESS.select{|c| c.in?(cols) }
290
335
  end
291
336
 
292
337
  # This should really be a method on AssociationReflection
@@ -300,9 +345,9 @@ module Hobo
300
345
  :has_many
301
346
  end
302
347
  refl.klass.reflections.values.find do |r|
303
- r.macro == reverse_macro and
304
- r.klass == self and
305
- !r.options[:conditions] and
348
+ r.macro == reverse_macro &&
349
+ r.klass == self &&
350
+ !r.options[:conditions] &&
306
351
  r.primary_key_name == refl.primary_key_name
307
352
  end
308
353
  end
@@ -457,6 +502,11 @@ module Hobo
457
502
  end
458
503
 
459
504
 
505
+ def dependent_on
506
+ self.class.dependent_on.map { |assoc| send(assoc) }
507
+ end
508
+
509
+
460
510
  def attributes_with_hobo_type_conversion=(attributes, guard_protected_attributes=true)
461
511
  converted = attributes.map_hash { |k, v| convert_type_for_mass_assignment(self.class.field_type(k), v) }
462
512
  send(:attributes_without_hobo_type_conversion=, converted, guard_protected_attributes)
@@ -465,7 +515,17 @@ module Hobo
465
515
 
466
516
 
467
517
  def set_creator(user)
468
- self.send("#{self.class.creator_attr}=", user) if (t = self.class.creator_type) && user.is_a?(t)
518
+ attr = self.class.creator_attribute
519
+ return unless attr
520
+
521
+ # Is creator a string field or an association?
522
+ if self.class.reflections[attr]
523
+ # It's an association
524
+ self.send("#{attr}=", user) if (t = self.class.creator_type) && user.is_a?(t)
525
+ else
526
+ # Assume it's a string field -- set it to the name of the current user
527
+ self.send("#{attr}=", user.to_s) unless user.guest?
528
+ end
469
529
  end
470
530
 
471
531
 
@@ -487,11 +547,15 @@ module Hobo
487
547
 
488
548
 
489
549
  def same_fields?(other, *fields)
550
+ return true if other.nil?
551
+
490
552
  fields = fields.flatten
491
553
  fields.all?{|f| self.send(f) == other.send(f)}
492
554
  end
493
555
 
494
556
  def only_changed_fields?(other, *changed_fields)
557
+ return true if other.nil?
558
+
495
559
  changed_fields = changed_fields.flatten.every(:to_s)
496
560
  all_cols = self.class.columns.every(:name) - []
497
561
  all_cols.all?{|c| c.in?(changed_fields) || self.send(c) == other.send(c) }
@@ -514,12 +578,10 @@ module Hobo
514
578
  end
515
579
 
516
580
  def to_s
517
- if respond_to? :title
518
- title
519
- elsif respond_to? :name
520
- name
581
+ if self.class.name_attribute
582
+ send self.class.name_attribute
521
583
  else
522
- "#{self.class.name.humanize} #{id}"
584
+ "#{self.class.name.titleize} #{id}"
523
585
  end
524
586
  end
525
587
 
@@ -4,18 +4,10 @@ module Hobo
4
4
 
5
5
  include Hobo::Controller
6
6
 
7
- class PermissionDeniedError < RuntimeError; end
8
- class UserPermissionError < StandardError
9
- attr :models
10
- def initialize(models)
11
- @models = models || []
12
- end
13
- end
14
-
15
7
  VIEWLIB_DIR = "taglibs"
16
8
 
17
9
  PAGINATE_FORMATS = [ Mime::HTML, Mime::ALL ]
18
-
10
+
19
11
  class << self
20
12
 
21
13
  def included(base)
@@ -23,38 +15,14 @@ module Hobo
23
15
  @auto_actions ||= {}
24
16
 
25
17
  extend ClassMethods
26
- helper_method :find_partial, :model, :current_user
18
+
19
+ helper_method :model, :current_user
27
20
  before_filter :set_no_cache_headers
28
21
  end
22
+ base.template_path_cache = {}
29
23
 
30
24
  Hobo::Controller.included_in_class(base)
31
25
  end
32
-
33
- def find_partial(klass, as)
34
- find_model_template(klass, as, :is_parital => true)
35
- end
36
-
37
-
38
- def template_path(dir, name, is_partial)
39
- fileRx = is_partial ? /^_#{name}\.[^.]+/ : /^#{name}\.[^.]+/
40
- full_dir = "#{RAILS_ROOT}/app/views/#{dir}"
41
- if File.exists?(full_dir) && Dir.entries(full_dir).grep(fileRx).any?
42
- return "#{dir}/#{name}"
43
- end
44
- end
45
-
46
-
47
- def find_model_template(klass, name, options={})
48
- while klass and klass != ActiveRecord::Base
49
- dir = klass.name.underscore.pluralize
50
- dir = File.join(options[:subsite], dir) if options[:subsite]
51
- path = template_path(dir, name, options[:is_partial])
52
- return path if path
53
-
54
- klass = klass.superclass
55
- end
56
- nil
57
- end
58
26
 
59
27
  end
60
28
 
@@ -62,6 +30,8 @@ module Hobo
62
30
 
63
31
  attr_writer :model
64
32
 
33
+ attr_accessor :template_path_cache
34
+
65
35
  def add_collection_actions(name)
66
36
  defined_methods = instance_methods
67
37
 
@@ -400,6 +370,24 @@ module Hobo
400
370
  end
401
371
 
402
372
 
373
+ def destination_after_create(record)
374
+ # The after_submit post parameter takes priority
375
+ params[:after_submit] ||
376
+
377
+ # Then try the records show page
378
+ object_url(@this, :if_available => true) ||
379
+
380
+ # Then the show page of the 'owning' object if there is one
381
+ (@this.dependent_on.first && object_url(@this.dependent_on.first, :if_available => true)) ||
382
+
383
+ # Last try - the index page for this model
384
+ object_url(@this.class, :if_available => true) ||
385
+
386
+ # Give up
387
+ home_page
388
+ end
389
+
390
+
403
391
  # --- Action implementations --- #
404
392
 
405
393
  def hobo_index(*args, &b)
@@ -478,7 +466,7 @@ module Hobo
478
466
  response_block(&b) or
479
467
  if valid?
480
468
  respond_to do |wants|
481
- wants.html { redirect_to(params[:after_submit] || object_url(@this)) }
469
+ wants.html { redirect_to(destination_after_create(@this)) }
482
470
  wants.js { hobo_ajax_response || render(:text => "") }
483
471
  end
484
472
  elsif invalid?
@@ -510,14 +498,14 @@ module Hobo
510
498
  @this.send(:clear_aggregation_cache)
511
499
  @this.send(:clear_association_cache)
512
500
 
513
- changes = params[model.name.underscore]
501
+ changes = params[@this.class.name.underscore]
514
502
  @this.attributes = changes
515
503
  save_and_set_status!(@this, original)
516
504
 
517
505
  # Ensure current_user isn't out of date
518
506
  @current_user = @this if @this == current_user
519
507
 
520
- flash[:notice] = "Changes to the #{model.name.titleize.downcase} were saved" if !request.xhr? && valid?
508
+ flash[:notice] = "Changes to the #{@this.class.name.titleize.downcase} were saved" if !request.xhr? && valid?
521
509
 
522
510
  set_named_this!
523
511
  response_block(&b) or
@@ -671,32 +659,43 @@ module Hobo
671
659
  end
672
660
  end
673
661
 
662
+
663
+ def hobo_template_exists?(dir, name)
664
+ self.class.template_path_cache.clear if RAILS_ENV == "development"
665
+ self.class.template_path_cache.fetch([dir, name],
666
+ begin
667
+ full_dir = "#{RAILS_ROOT}/app/views/#{dir}"
668
+ !Dir["#{full_dir}/#{name}.*"].empty?
669
+ end)
670
+ end
671
+
672
+
673
+ def find_model_template(klass, name)
674
+ while klass and klass != ActiveRecord::Base
675
+ dir = klass.name.underscore.pluralize
676
+ dir = File.join(subsite, dir) if subsite
677
+ if hobo_template_exists?(dir, name)
678
+ return "#{dir}/#{name}"
679
+ end
680
+ klass = klass.superclass
681
+ end
682
+ nil
683
+ end
674
684
 
685
+
675
686
  def hobo_render(page_kind = nil, page_model=nil)
676
687
  page_kind ||= params[:action].to_sym
677
688
  page_model ||= model
678
-
679
- template = ModelController.find_model_template(page_model, page_kind, :subsite => subsite)
680
689
 
681
- begin
682
- if template
683
- render :template => template
684
- true
685
- else
686
- # This returns false if no such tag exists
687
- render_tag("#{page_kind.to_s.dasherize}-page", :with => @this)
688
- end
689
- rescue ActionView::TemplateError => wrapper
690
- e = wrapper.original_exception if wrapper.respond_to? :original_exception
691
- if e.is_a? Hobo::ModelController::UserPermissionError
692
- if current_user.guest?
693
- redirect_to login_url(e.models.first || UserController.user_models.first)
694
- else
695
- permission_denied(:message => e.message)
696
- end
697
- else
698
- raise
699
- end
690
+ if hobo_template_exists?(controller_path, page_kind)
691
+ render :action => page_kind
692
+ true
693
+ elsif (template = find_model_template(page_model, page_kind))
694
+ render :template => template
695
+ true
696
+ else
697
+ # This returns false if no such tag exists
698
+ render_tag("#{page_kind.to_s.dasherize}-page", :with => @this)
700
699
  end
701
700
  end
702
701
 
@@ -1,13 +1,24 @@
1
+ class ActionController::Routing::RouteSet
2
+ # Monkey patch this method so routes are reloaded on *every*
3
+ # request. Without this Rails checks the mtime of config/routes.rb
4
+ # which doesn't take into account Hobo's auto routing
5
+ def reload
6
+ load!
7
+ end
8
+ end
9
+
1
10
  module Hobo
2
11
 
3
12
  class ModelRouter
4
13
 
5
- @linkable = Hash.new {|h, k| h[k] = Hash.new {|h, k| h[k] = {} } }
6
-
7
14
  APP_ROOT = "#{RAILS_ROOT}/app"
8
15
 
9
16
  class << self
10
17
 
18
+ def reset_linkables
19
+ @linkable = Hash.new {|h, k| h[k] = Hash.new {|h, k| h[k] = {} } }
20
+ end
21
+
11
22
  def linkable(subsite, klass, action)
12
23
  @linkable[subsite][klass.name][action] = true
13
24
  end
@@ -17,6 +28,7 @@ module Hobo
17
28
  end
18
29
 
19
30
  def add_routes(map)
31
+ reset_linkables
20
32
  begin
21
33
  ActiveRecord::Base.connection.reconnect! unless ActiveRecord::Base.connection.active?
22
34
  rescue
@@ -25,8 +37,8 @@ module Hobo
25
37
  end
26
38
 
27
39
  require "#{APP_ROOT}/controllers/application" unless Object.const_defined? :ApplicationController
28
- require "#{APP_ROOT}/assemble.rb" if File.exists? "#{APP_ROOT}/assemble.rb"
29
-
40
+ load "#{APP_ROOT}/assemble.rb" if File.exists? "#{APP_ROOT}/assemble.rb"
41
+
30
42
  # Add non-subsite routes
31
43
  add_routes_for(map, nil)
32
44
 
@@ -11,6 +11,7 @@ module Hobo::RapidHelper
11
11
  js_options['resultUpdate'] = js_result_updates(options[:result_update]) if options[:result_update]
12
12
  js_options['resetForm'] = options[:reset_form] if options.has_key?(:reset_form)
13
13
  js_options['refocusForm'] = options[:refocus_form] if options.has_key?(:refocus_form)
14
+ js_options['spinnerNextTo'] = options[:spinner_next_to] if options.has_key?(:spinner_next_to)
14
15
 
15
16
  js_options.empty? ? nil : options_for_javascript(js_options)
16
17
  end
@@ -97,7 +98,7 @@ module Hobo::RapidHelper
97
98
 
98
99
  AJAX_ATTRS = [:before, :success, :failure, :complete, :type, :method,
99
100
  :script, :form, :params, :confirm,
100
- :reset_form, :refocus_form, :result_update]
101
+ :reset_form, :refocus_form, :result_update, :spinner_next_to]
101
102
 
102
103
 
103
104
  def editor_class
@@ -41,7 +41,7 @@ module Hobo
41
41
  end
42
42
  end
43
43
 
44
- # Additional classmethods for AuthenticatedUser
44
+ # Additional classmethods for authentication
45
45
  module ClassMethods
46
46
 
47
47
  # Validation of the plaintext password
@@ -49,22 +49,20 @@ module Hobo
49
49
  validates_length_of :password, :within => 4..40, :if => :password_required?
50
50
  end
51
51
 
52
- def set_login_attr(attr)
52
+ def login_attribute=(attr, validate=true)
53
53
  @login_attr = attr = attr.to_sym
54
54
  unless attr == :login
55
55
  alias_attribute(:login, attr)
56
56
  set_field_type :login => field_type(attr)
57
57
  end
58
58
 
59
- if block_given?
60
- yield
61
- else
59
+ if validate
62
60
  validates_presence_of attr
63
61
  validates_length_of attr, :within => 3..100
64
62
  validates_uniqueness_of attr, :case_sensitive => false
65
63
  end
66
64
  end
67
- def login_attr; @login_attr; end
65
+ attr_reader :login_attr
68
66
 
69
67
  # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
70
68
  def authenticate(login, password)
@@ -86,6 +84,11 @@ module Hobo
86
84
  def encrypt(password, salt)
87
85
  Digest::SHA1.hexdigest("--#{salt}--#{password}--")
88
86
  end
87
+
88
+
89
+ def set_admin_on_first_user
90
+ before_create { |user| user.administrator = true if count == 0 }
91
+ end
89
92
 
90
93
  end
91
94
 
@@ -121,7 +124,7 @@ module Hobo
121
124
  def guest?
122
125
  false
123
126
  end
124
-
127
+
125
128
  protected
126
129
  # Before filter that encrypts the password before having it stored in the database.
127
130
  def encrypt_password