lib/goaloc/goal.rb in mattknox-goaloc-0.4.5 vs lib/goaloc/goal.rb in mattknox-goaloc-0.4.7
- old
+ new
@@ -1,16 +1,21 @@
# This is the core of goaloc: the goal object.
# A goal object is intended to be a vertical slice of an MVC app, with information about its model, views,
# controllers, and routes. It needs to be extended so that it can handle generating plugins/stylesheets/js/etc.
# Generators call a number of methods on goals that allow them to hook into their code generation process and
# customize the output.
+
+# TODO: how should I let a goal know that it should generate, eg, its models with datamapper and views with haml?
+# it would be nice to have prototypical inheritance here, where I'd make a generator object that inherits all
+# of the state of the model, and has extra logic to tell it to generate datamapper files.
+
class Goal
attr_reader :name
attr_accessor :associations, :validations, :fields, :options, :routes, :foreign_keys
def initialize(name, route = nil)
- @name = name.underscore.singularize # TODO: support renaming models
+ @name = name.underscore.singularize
self.associations = HashWithIndifferentAccess.new
self.validations = []
self.fields = HashWithIndifferentAccess.new
self.foreign_keys = HashWithIndifferentAccess.new
self.options = { }
@@ -19,10 +24,20 @@
Object.send(:remove_const, self.cs)
end
Object.const_set self.cs, self
end
+# TODO: support renaming models
+# # this is a bad thing to do until it actually supports pervasive renaming (assocs, etc.)
+# def name=(name)
+# if Object.const_defined? self.cs and name != self.name
+# Object.send(:remove_const, self.cs)
+# end
+# @name = name
+# Object.const_set self.cs, self
+# end
+
# === here are a list of name-ish methods
# This returns the name of the foreign key used to refer to this goal.
def foreign_key
self.name + "_id"
end
@@ -40,29 +55,55 @@
end
# === stuff used to introspect on the goal
# thanks to Josh Ladieu for this: it's the array of things needed to get to an instance of this class, if there is a unique set.
- def resource_tuple # this returns the minimal route to this goal, or nothing, if there is no unambiguous minimal route
+ # this returns the minimal route to this goal, or nothing, if there is no unambiguous minimal route
+ def resource_tuple
routelist = self.routes.sort { |x, y| x.length <=> y.length }
if routelist.length == 1 #TODO: maybe should deal with a case where there's a simplest route that all the others contain.
routelist.first
else
[]
end
end
+ # returns a list of all the params that are required for a goal but not inferrable from the path in which it is encountered.
+ def required_nonpath_params
+ self.associations.reject { |k,v| v[:type] != :belongs_to }.keys.reject {|x| self.resource_tuple.map { |y| y.to_s.singularize }.member?(x) }
+ end
+
+ # if a resource has a resource_tuple, and it is not the first element of that
+ # tuple, this will return true.
def nested?
self.resource_tuple.length > 1
end
+ # enclosing_resource returns the resource that most directly encloses this resource
+ def enclosing_resource
+ self.resource_tuple[-2]
+ end
+
+ # enclosing_resources returns the whole chain of resources up to but not including this one.
+ def enclosing_resources
+ self.resource_tuple[0..-2]
+ end
+
+ def enclosing_goal
+ self.enclosing_resource.to_s.singularize.camelize.constantize
+ end
+
+ def enclosing_goals
+ self.enclosing_resources.map { |r| r.to_s.singularize.camelize.constantize }
+ end
+
def underscore_tuple
- self.resource_tuple.to_a.map { |x| x.to_s.underscore.singularize }
+ self.resource_tuple.map { |x| x.to_s.underscore.singularize }
end
def ivar_tuple
- self.resource_tuple.to_a.map { |x| "@" + x.to_s.underscore.singularize }
+ self.resource_tuple.map { |x| "@" + x.to_s.underscore.singularize }
end
# this is intended to grab the list of elements needed to populate a form_for,
# propagated back from the named end_element
# so for [:users, [:posts, [:comments, :ratings]]] in the rating form it would be:
@@ -71,10 +112,11 @@
self.resource_tuple[0..-2].map {|sym| sym.to_s.singularize }.reverse.inject([end_element]) {|acc, x| acc.unshift(acc.first + "." + x )}
end
# this returns the set of resources that are nested under this resource.
def nested_resources
- APP.goals.reject { |k, v| (v.routes != [(self.resource_tuple + [k.pluralize.to_sym])]) and (v.routes != [(self.resource_tuple + [k.singularize.to_sym])]) } # TODO: see if this can be cleaned up a bit.
+ APP.goals.reject { |k, v| (v.routes != [(self.resource_tuple + [k.pluralize.to_sym])]) and
+ (v.routes != [(self.resource_tuple + [k.singularize.to_sym])]) } # TODO: see if this can be cleaned up a bit.
end
# validations
def validates(validation_type, field, opts = { })
self.validations << opts.merge({ :val_type => validation_type, :field => field})