casey_jones 0.0.99
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -0
- data/README +59 -0
- data/Rakefile +70 -0
- data/install.rb +1 -0
- data/lib/acts_as_linkable/acts_as_linkable.rb +34 -0
- data/lib/ajax_loading/ajax_loading.rb +2 -0
- data/lib/ajax_loading/controller_resource.rb +37 -0
- data/lib/anaf_active_record.rb +45 -0
- data/lib/casey_jones.rb +8 -0
- data/lib/casey_jones/active_record_extensions.rb +160 -0
- data/lib/casey_jones/application_helper_methods.rb +39 -0
- data/lib/casey_jones/string_reader.rb +113 -0
- data/lib/generators/anaf_habtm/anaf_habtm_generator.rb +11 -0
- data/lib/generators/anaf_habtm/assets/nested_attributes.css +3 -0
- data/lib/generators/anaf_habtm/assets/nested_attributes.js +73 -0
- data/test/anaf_habtm_test.rb +8 -0
- data/test/test_helper.rb +3 -0
- data/uninstall.rb +1 -0
- metadata +85 -0
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
== HABTM Example
|
2
|
+
|
3
|
+
Complex forms on a HABTM association.
|
4
|
+
|
5
|
+
Using code borrowed from Pat (http://patshaughnessy.net), whose view_mapper
|
6
|
+
finally taught this hobbyist hacker to write forms.
|
7
|
+
|
8
|
+
Hopefully this will fit snugly into someone else's gem
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
* Install as a gem:
|
13
|
+
# Add to your Gemfile
|
14
|
+
gem "anaf_habtm"
|
15
|
+
* or as a plugin
|
16
|
+
rails plugin install git://github.com/tylergannon/anaf_habtm.git
|
17
|
+
|
18
|
+
Run the generator to place the javascript in the right place:
|
19
|
+
|
20
|
+
rails generate anaf_habtm
|
21
|
+
|
22
|
+
Add the following to your layout:
|
23
|
+
<%= javascript_include_tag 'nested_attributes.js' %>
|
24
|
+
|
25
|
+
== Usage
|
26
|
+
|
27
|
+
Inside your model, call anaf_habtm just as you would call has_and_belongs_to_many,
|
28
|
+
except now you need to offer a code block telling rails what to do with each object.
|
29
|
+
The plugin will handle deleting stuff.
|
30
|
+
|
31
|
+
# Basically, your code block needs to return an object of the correct
|
32
|
+
# type for your association collection, or else nil if your code
|
33
|
+
# determines that the object should be rejected.
|
34
|
+
class Customer < ActiveRecord::Base
|
35
|
+
anaf_habtm :orders, :autosave => true, :uniq => false do |params, order|
|
36
|
+
order = order ||= Order.new
|
37
|
+
order.attributes = params
|
38
|
+
logger.error params.inspect
|
39
|
+
order
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Order < ActiveRecord::Base
|
44
|
+
has_and_belongs_to_many :customers
|
45
|
+
anaf_habtm :items, :autosave => true, :uniq => false do |params, item|
|
46
|
+
logger.error "ITEM::: #{params.inspect}"
|
47
|
+
item ||= Item.find_or_create_by_name(params["name"])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
== Additional info
|
52
|
+
|
53
|
+
See the example app at http://github.com/tylergannon/accepts-nested-attributes-habtm-example for more details.
|
54
|
+
|
55
|
+
Also check out Pat's view_mapper... it can generate the code you need inside your views.
|
56
|
+
|
57
|
+
http://github.com/patshaughnessy/view_mapper
|
58
|
+
|
59
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
PKG_FILES = FileList[
|
3
|
+
'anaf_habtm.gemspec',
|
4
|
+
'Gemfile',
|
5
|
+
'install.rb',
|
6
|
+
'Rakefile', 'README', 'uninstall.rb',
|
7
|
+
'assets/**/*',
|
8
|
+
'lib/**/*',
|
9
|
+
'rails/**/*',
|
10
|
+
'test/**/*'
|
11
|
+
]
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'jeweler'
|
18
|
+
Jeweler::Tasks.new do |s|
|
19
|
+
s.name = "casey_jones"
|
20
|
+
s.author = "Tyler Gannon"
|
21
|
+
s.email = "t--g__a--nnon@gmail.com"
|
22
|
+
s.homepage = "http://github.com/tylergannon/anaf_habtm"
|
23
|
+
s.platform = Gem::Platform::RUBY
|
24
|
+
s.description = "some extra stuff for rails apps"
|
25
|
+
s.summary = "My favorite rails goodies all in one"
|
26
|
+
s.files = PKG_FILES.to_a
|
27
|
+
s.require_path = "lib"
|
28
|
+
s.has_rdoc = false
|
29
|
+
s.extra_rdoc_files = ["README"]
|
30
|
+
end
|
31
|
+
|
32
|
+
Jeweler::GemcutterTasks.new
|
33
|
+
rescue LoadError
|
34
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'rake/testtask'
|
38
|
+
Rake::TestTask.new(:test) do |test|
|
39
|
+
test.libs << 'lib' << 'test'
|
40
|
+
test.pattern = 'test/**/test_*.rb'
|
41
|
+
test.verbose = true
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'rcov/rcovtask'
|
46
|
+
Rcov::RcovTask.new do |test|
|
47
|
+
test.libs << 'test'
|
48
|
+
test.pattern = 'test/**/test_*.rb'
|
49
|
+
test.verbose = true
|
50
|
+
end
|
51
|
+
rescue LoadError
|
52
|
+
task :rcov do
|
53
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
task :test => :check_dependencies
|
58
|
+
|
59
|
+
task :default => :test
|
60
|
+
|
61
|
+
require 'rake/rdoctask'
|
62
|
+
Rake::RDocTask.new do |rdoc|
|
63
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
64
|
+
|
65
|
+
rdoc.rdoc_dir = 'rdoc'
|
66
|
+
rdoc.title = "da_huangs_ruby_extensions #{version}"
|
67
|
+
rdoc.rdoc_files.include('README*')
|
68
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
69
|
+
end
|
70
|
+
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActsAsLinkable
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
def acts_as_linkable(opts={}, &block)
|
4
|
+
class_eval "def link_attr; #{opts.inspect}; end;"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module ActionViewExtensions
|
9
|
+
def link(obj)
|
10
|
+
raise "I can't link to nil." unless obj
|
11
|
+
if (obj.class == Array) || (obj.class == ActiveRecord::Relation)
|
12
|
+
obj.map{|v| link(v)}.to_sentence
|
13
|
+
else
|
14
|
+
p = obj.link_attr
|
15
|
+
p[:partial] = "#{obj.class.name.tableize}/link" if p.empty?
|
16
|
+
unless p[:partial].nil?
|
17
|
+
obj_name = (p[:object_name] ||= obj.class.name.underscore).to_s.to_sym
|
18
|
+
(render :partial => p[:partial], :locals => {obj_name => obj}).strip
|
19
|
+
else
|
20
|
+
opts = {}
|
21
|
+
link_attr = obj.link_attr
|
22
|
+
link_attr.each do |key, val|
|
23
|
+
link_attr[key] = obj.send(val) if val.class == Symbol
|
24
|
+
end
|
25
|
+
opts[:title] = link_attr[:title] if link_attr.has_key?(:title)
|
26
|
+
link_to link_attr[:name], obj, opts
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
ActiveRecord::Base.extend(ActsAsLinkable::ActiveRecordExtensions)
|
33
|
+
ActionView::Base.send :include, ActsAsLinkable::ActionViewExtensions
|
34
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module AjaxLoading
|
2
|
+
class ControllerResource < CanCan::ControllerResource
|
3
|
+
def initialize(controller, name, parent = nil, options = {})
|
4
|
+
super(controller, name, parent, options)
|
5
|
+
end
|
6
|
+
|
7
|
+
def load_resource
|
8
|
+
puts "AjaxLoading::load_resource"
|
9
|
+
params = @controller.params
|
10
|
+
if params.has_key?(:associated)
|
11
|
+
ass_params = params[:associated]
|
12
|
+
relation = ass_params["class_name"].constantize.find(ass_params["id"])
|
13
|
+
association = ass_params["association"]
|
14
|
+
instance_variable_set 'container',
|
15
|
+
ass_params["container"] ||= relation.element_id(:show, association)
|
16
|
+
|
17
|
+
self.model_instance = relation.send(association)
|
18
|
+
elsif collection_action?
|
19
|
+
self.model_instance = model_class.where('1=1')
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def instance_variable_set(name, value)
|
28
|
+
name = '@'+name unless name.include?('@')
|
29
|
+
@controller.instance_variable_set(name, value)
|
30
|
+
end
|
31
|
+
def instance_variable_get(name)
|
32
|
+
name = '@'+name unless name.include?('@')
|
33
|
+
@controller.instance_variable_get(name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
module AnafHabtm
|
3
|
+
module ActiveRecord
|
4
|
+
def anaf_habtm(association, options={}, &block)
|
5
|
+
class_eval do
|
6
|
+
# Define a proc that will look up the (potentially) existing object
|
7
|
+
finder = proc {|id| association.to_s.singularize.camelize.constantize.where(:id=>id).first
|
8
|
+
}
|
9
|
+
|
10
|
+
# Define a proc that will set the association collection
|
11
|
+
set_collection = proc {|me, coll| me.send("#{association.to_s.tableize}=", coll)}
|
12
|
+
has_and_belongs_to_many association.to_sym, options
|
13
|
+
# Define the actual association setter.
|
14
|
+
define_method "#{association.to_s.tableize}_attributes=", lambda{|attributes_for_association|
|
15
|
+
coll = []
|
16
|
+
|
17
|
+
attributes_for_association.each_value do |params|
|
18
|
+
next if params["_destroy"] == "1"
|
19
|
+
obj = finder.call(params["id"]) if params.has_key?("id")
|
20
|
+
params.extend(AnafHabtm::HashExtension)
|
21
|
+
# ActiveRecord::Base.attributes=() doesn't like extra parameters.
|
22
|
+
coll << block.call(params.copy_without_destroy, obj)
|
23
|
+
end
|
24
|
+
set_collection.call(self, coll)
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def named_association(member, attribute, opts={})
|
30
|
+
member = member.to_s
|
31
|
+
klass = opts.has_key?(:class) ? opts[:class_name] : member.to_s.singularize.camelize
|
32
|
+
attribute = attribute.to_s
|
33
|
+
if opts.has_key?(:create)
|
34
|
+
class_eval "def #{member}_#{attribute}=(#{attribute});
|
35
|
+
return if #{attribute}.blank?
|
36
|
+
self.#{member} = #{klass}.find_or_create_by_#{attribute}(#{attribute})
|
37
|
+
end;"
|
38
|
+
else
|
39
|
+
class_eval "def #{member}_#{attribute}=(#{attribute}); self.#{member} = #{klass}.find_by_#{attribute}(#{attribute}) unless #{attribute}.blank?; end;"
|
40
|
+
end
|
41
|
+
class_eval "def #{member}_#{attribute}; #{member}.#{attribute} if #{member}; end;"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/lib/casey_jones.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'casey_jones/active_record_extensions'
|
2
|
+
require 'casey_jones/application_helper_methods'
|
3
|
+
require 'casey_jones/string_reader'
|
4
|
+
require 'ajax_loading/ajax_loading'
|
5
|
+
require 'acts_as_linkable/acts_as_linkable'
|
6
|
+
ActiveRecord::Base.extend(CaseyJones::ActiveRecord)
|
7
|
+
ActionView::Base.send :include, CaseyJones::ApplicationHelperMethods
|
8
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
|
2
|
+
module CaseyJones
|
3
|
+
module ActiveRecord
|
4
|
+
def autocomplete_format(&block)
|
5
|
+
class_eval do
|
6
|
+
@autocomplete_block = block
|
7
|
+
def self.to_autocomplete(items)
|
8
|
+
items.map{|t| @autocomplete_block.call(t)}.to_json
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Needs to call scopes on a class and pass it values that are
|
14
|
+
# pulled from a hash called obj_attr
|
15
|
+
def make_scope_string(find_opts)
|
16
|
+
_scopes = []
|
17
|
+
find_opts.each{ |scope, attr_name|
|
18
|
+
if attr_name.class == Hash
|
19
|
+
_scopes << "#{scope.to_s}(obj_attr[#{name.first[0].to_s}][#{name.first[1].to_s}])"
|
20
|
+
else
|
21
|
+
_scopes << "#{scope.to_s}(obj_attr[#{name.to_s}])"
|
22
|
+
end
|
23
|
+
}
|
24
|
+
_scopes.join(".")
|
25
|
+
end
|
26
|
+
|
27
|
+
def anaf_habtm(association, options={})
|
28
|
+
ar_opts = options[:ar_options] ||= {}
|
29
|
+
klass = ar_opts[:class_name] ||= association.to_s.singularize.camelize
|
30
|
+
class_eval do
|
31
|
+
has_and_belongs_to_many association.to_sym, ar_opts
|
32
|
+
end
|
33
|
+
find_line = "obj = obj ||= #{klass}.#{make_scope_string(options[:find])}.first" if options.has_key?(:find)
|
34
|
+
association = association.to_s.underscore.tableize
|
35
|
+
scope_string =
|
36
|
+
class_eval <<END
|
37
|
+
def #{association}_attributes=(attr_hash)
|
38
|
+
obj_coll = []
|
39
|
+
attr_hash.each_value do |obj_attr|
|
40
|
+
next if obj_attr["_destroy"] == "1"
|
41
|
+
obj = #{klass}.find(obj_attr["id"]) if obj_attr.has_key?("id")
|
42
|
+
obj_attr = obj_attr.reject_keys ["id", "_destroy"]
|
43
|
+
#{find_line}
|
44
|
+
if obj && obj.update_attributes(obj_attr)
|
45
|
+
obj_coll << obj
|
46
|
+
elsif (obj = #{klass}.new(obj_attributes)) && obj.save
|
47
|
+
obj_coll << obj
|
48
|
+
end
|
49
|
+
end
|
50
|
+
self.#{association} = obj_coll
|
51
|
+
end
|
52
|
+
END
|
53
|
+
end
|
54
|
+
|
55
|
+
def association_text(association, opts={})
|
56
|
+
raise "Must give a name for text_association" unless opts.has_key?(:name)
|
57
|
+
class_eval do
|
58
|
+
if opts.has_key?(:class_name)
|
59
|
+
klass = opts[:class_name].constantize
|
60
|
+
else
|
61
|
+
klass = association.to_s.singularize.camelize.constantize
|
62
|
+
end
|
63
|
+
define_method "#{association.to_s}_text=", lambda{ |text|
|
64
|
+
return if text.empty?
|
65
|
+
coll = StringReader.new.read_items(text) do |name, commentary|
|
66
|
+
a = klass.undecorate(name) if klass.methods.include?("undecorate")
|
67
|
+
if opts.has_key?(:scope)
|
68
|
+
obj = self.send(association).scopes[opts[:scope]].call(name).first
|
69
|
+
end
|
70
|
+
params = {opts[:name]=>name}
|
71
|
+
params[opts[:commentary]] = commentary if opts.has_key?(:commentary)
|
72
|
+
obj = obj ||= klass.new
|
73
|
+
obj = klass.find(obj) unless obj.new_record?
|
74
|
+
obj.decoration = a if a
|
75
|
+
obj.attributes = params
|
76
|
+
obj.save
|
77
|
+
obj
|
78
|
+
end
|
79
|
+
self.send("#{association.to_s}=", coll)
|
80
|
+
}
|
81
|
+
|
82
|
+
define_method "#{association.to_s}_text", lambda{
|
83
|
+
StringReader.new.write_items(self.send(association.to_s)) do |item|
|
84
|
+
name = item.send(opts[:name])
|
85
|
+
name = item.decorate(name) if item.methods.include?("decorate")
|
86
|
+
[name, opts.has_key?(:commentary) ? item.send(opts[:commentary]) : ""]
|
87
|
+
end
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def named_association(member, attribute, opts={})
|
93
|
+
member = member.to_s
|
94
|
+
klass = (opts.has_key?(:class_name) ? opts[:class_name] : member.to_s.singularize.camelize).constantize
|
95
|
+
attribute = attribute.to_s
|
96
|
+
if opts.has_key?(:create)
|
97
|
+
class_eval do
|
98
|
+
define_method "#{member}_#{attribute}=", lambda{|value|
|
99
|
+
return if value.blank?
|
100
|
+
obj = klass.named(value)
|
101
|
+
obj = obj ||= klass.create(attribute => value)
|
102
|
+
self.send("#{member}=", obj)
|
103
|
+
}
|
104
|
+
end
|
105
|
+
else
|
106
|
+
class_eval do
|
107
|
+
define_method "#{member}_#{attribute}=", lambda{|value|
|
108
|
+
self.send("#{member}=",klass.named(value)) unless value.blank?
|
109
|
+
}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
class_eval "def #{member}_#{attribute};
|
113
|
+
#{member}.#{attribute} if #{member};
|
114
|
+
end;"
|
115
|
+
end
|
116
|
+
|
117
|
+
def search_on(*cols)
|
118
|
+
class_eval "def self.search_columns; #{cols.map{|t| t.to_s}.to_ary.inspect}; end;"
|
119
|
+
class_eval do
|
120
|
+
scope :search, lambda{|str|
|
121
|
+
items = like_condition(str.downcase)
|
122
|
+
if scopes.has_key?(:search_mod)
|
123
|
+
items = items.search_mod
|
124
|
+
end
|
125
|
+
items
|
126
|
+
}
|
127
|
+
scope :with_name, lambda{|str|
|
128
|
+
equals_condition(str.downcase).limit(1)
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def lookup(params)
|
134
|
+
str = params[:id]
|
135
|
+
if str.match(/\D/)
|
136
|
+
named(str)
|
137
|
+
else
|
138
|
+
find(str)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def like_condition(str)
|
143
|
+
where(condition("ilike '%#{str}%'"))
|
144
|
+
end
|
145
|
+
|
146
|
+
def equals_condition(str)
|
147
|
+
where(condition("= '#{str.downcase}'"))
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def named(str)
|
152
|
+
with_name(str).first
|
153
|
+
end
|
154
|
+
|
155
|
+
def condition(cond)
|
156
|
+
search_columns.map{|c| "trim(lower(#{c})) #{cond}"}.join(" or ")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module CaseyJones
|
2
|
+
module ApplicationHelperMethods
|
3
|
+
def remove_child_link(name, form_builder)
|
4
|
+
value = form_builder.object.new_record? ? "1" : "false"
|
5
|
+
klass = form_builder.object.class.name.underscore
|
6
|
+
form_builder.hidden_field(:_destroy, {:value=>value, "data-remove"=>klass}) +
|
7
|
+
link_to(name, options={}, html_options={"href"=>"#", "class"=>"remove-child-link", :tabindex=> "0"})
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_child_link(name, child, form_builder, options={})
|
11
|
+
# puts "||#{form_builder}||"
|
12
|
+
new_form = new_child_fields(child, form_builder, options)
|
13
|
+
raw(content_tag(:div, new_form, {:id=>"#{child}_template", :class=>"form-template"}, escape=false))+
|
14
|
+
link_to(name, options={}, html_options={"href"=>"#", "class"=>"add-child-link", "data-class-name"=>child})
|
15
|
+
end
|
16
|
+
|
17
|
+
def new_child_fields(child, form_builder, options={})
|
18
|
+
partial = options[:partial] ||= child.underscore
|
19
|
+
output = ""
|
20
|
+
form_builder.fields_for(child.pluralize.to_sym, child.camelize.constantize.new, :child_index => "__#{child}_id__") do |f|
|
21
|
+
output += render(:partial => partial, :locals => { :f => f })
|
22
|
+
end
|
23
|
+
output
|
24
|
+
end
|
25
|
+
|
26
|
+
def tfwac(f, field, controller, opts={})
|
27
|
+
opts["data-auto-complete"]='tf'
|
28
|
+
opts["data-auto-complete-url"]=eval("#{controller.to_s.underscore.tableize}_path(:format=>:json)")
|
29
|
+
f.text_field field, opts
|
30
|
+
end
|
31
|
+
|
32
|
+
def text_area_with_auto_complete(f, field, controller, opts={})
|
33
|
+
opts["data-auto-complete"]='ta'
|
34
|
+
opts["data-auto-complete-url"]=eval("#{controller.to_s.underscore.tableize}_path(:format=>:json)")
|
35
|
+
f.text_area field, opts
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module AnafHabtm
|
2
|
+
class StringReader
|
3
|
+
KEY_SYMPTOM = "*"
|
4
|
+
POSS_SYMPTOM = "-"
|
5
|
+
START_COMMENT = "{"
|
6
|
+
ONE_LINE_COMMENT = "/"
|
7
|
+
END_COMMENT = "}"
|
8
|
+
NEWLINE = "\n"
|
9
|
+
|
10
|
+
DELIM = [START_COMMENT, NEWLINE, ONE_LINE_COMMENT, END_COMMENT]
|
11
|
+
|
12
|
+
MULTI_LINE = :multi_line
|
13
|
+
ONE_LINE = :one_line
|
14
|
+
NAME = :name
|
15
|
+
COMMENT = [MULTI_LINE, ONE_LINE]
|
16
|
+
|
17
|
+
@item = ""
|
18
|
+
@comment = nil
|
19
|
+
@items = []
|
20
|
+
|
21
|
+
def save(block)
|
22
|
+
unless @item.strip.empty?
|
23
|
+
@items << block.call(@item.strip, @comment.nil? ? nil : @comment.strip)
|
24
|
+
end
|
25
|
+
@item = ""
|
26
|
+
@comment = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def write_items(coll, &block)
|
30
|
+
lines = []
|
31
|
+
coll.each do |obj|
|
32
|
+
item, comment = block.call(obj)
|
33
|
+
if comment.nil? || comment.empty?
|
34
|
+
lines << item
|
35
|
+
elsif comment.index("/") || comment.index("\n")
|
36
|
+
lines << "#{item} {#{comment}}"
|
37
|
+
else
|
38
|
+
lines << "#{item} / #{comment}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
lines.join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_items(str, &block)
|
45
|
+
@items = []
|
46
|
+
buffer = ""
|
47
|
+
@item = ""
|
48
|
+
@comment = nil
|
49
|
+
state = NAME
|
50
|
+
|
51
|
+
str.chars.each do |char|
|
52
|
+
if (char == "/") && (state != MULTI_LINE)
|
53
|
+
@item = buffer.strip
|
54
|
+
buffer = ""
|
55
|
+
state = ONE_LINE
|
56
|
+
elsif char == "{"
|
57
|
+
@item = buffer.strip
|
58
|
+
buffer = ""
|
59
|
+
state = MULTI_LINE
|
60
|
+
elsif char == "}"
|
61
|
+
@comment = buffer.strip unless buffer.strip.empty?
|
62
|
+
buffer = ""
|
63
|
+
save(block)
|
64
|
+
state = NAME
|
65
|
+
elsif char == "\n"
|
66
|
+
if state == NAME
|
67
|
+
@item = buffer.strip
|
68
|
+
buffer = ""
|
69
|
+
save(block)
|
70
|
+
elsif state == ONE_LINE
|
71
|
+
@comment = buffer.strip
|
72
|
+
state = NAME
|
73
|
+
buffer = ""
|
74
|
+
save(block)
|
75
|
+
else
|
76
|
+
buffer << char
|
77
|
+
end
|
78
|
+
else
|
79
|
+
buffer << char
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if state == ONE_LINE
|
84
|
+
@comment = buffer
|
85
|
+
elsif state == MULTI_LINE
|
86
|
+
@comment = buffer
|
87
|
+
else
|
88
|
+
@item = buffer
|
89
|
+
end
|
90
|
+
save(block)
|
91
|
+
@items
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.parse_symptom(obj, symptom)
|
95
|
+
if symptom.index(KEY_SYMPTOM) == 0
|
96
|
+
obj.key_symptom = true
|
97
|
+
obj.symptom_name = symptom[KEY_SYMPTOM.length..symptom.length]
|
98
|
+
elsif symptom.index(POSS_SYMPTOM) == 0
|
99
|
+
obj.maybe = true
|
100
|
+
obj.symptom_name = symptom[POSS_SYMPTOM.length..symptom.length]
|
101
|
+
else
|
102
|
+
obj.symptom_name = symptom
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.decorate_symptom(obj)
|
107
|
+
decorator = ""
|
108
|
+
decorator = KEY_SYMPTOM if obj.key_symptom
|
109
|
+
decorator = POSS_SYMPTOM if obj.maybe
|
110
|
+
"#{decorator}#{obj.symptom_name}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class AnafHabtmGenerator < Rails::Generators::Base
|
4
|
+
self.source_root File.expand_path("assets", File.dirname(__FILE__))
|
5
|
+
|
6
|
+
def copy_initializer_file
|
7
|
+
copy_file "nested_attributes.js", "public/javascripts/nested_attributes.js"
|
8
|
+
copy_file "nested_attributes.css", "public/stylesheets/nested_attributes.css"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
function setUpDocument($jq) {
|
3
|
+
$jq.find("input[data-auto-complete|=tf]").each( function(idx, el){
|
4
|
+
$(el).autocomplete({
|
5
|
+
source: $(el).attr('data-auto-complete-url'),
|
6
|
+
minLength: 2
|
7
|
+
});
|
8
|
+
});
|
9
|
+
|
10
|
+
$jq.find("textarea[data-auto-complete|=ta]").each( function(idx, el){
|
11
|
+
setTextAreaAutoComplete($(el));
|
12
|
+
});
|
13
|
+
|
14
|
+
$jq.find("a.add-child-link").click( function() {
|
15
|
+
klass = $(this).attr('data-class-name');
|
16
|
+
$el = $(this).prev().children().first().clone();
|
17
|
+
new_id=new Date().getTime()
|
18
|
+
$el.html($el.html().replace(new RegExp('__'+klass+'_id__', 'g'), new_id));
|
19
|
+
$el.find('input[data-remove|='+klass+']').attr('value', 'false');
|
20
|
+
$(this).closest('.child').find('.'+klass+'_children').first()
|
21
|
+
.append($el).show();
|
22
|
+
setUpDocument($el);
|
23
|
+
return false;
|
24
|
+
});
|
25
|
+
|
26
|
+
$jq.find('a.remove-child-link').click( function() {
|
27
|
+
$(this).prev('input[type|=hidden]').val('1');
|
28
|
+
$(this).closest('.child').hide();
|
29
|
+
return false;
|
30
|
+
});
|
31
|
+
}
|
32
|
+
|
33
|
+
$(function () {
|
34
|
+
setUpDocument($("body"));
|
35
|
+
|
36
|
+
});
|
37
|
+
var text_area_delimiter = '\n';
|
38
|
+
function split(val) {
|
39
|
+
return val.split(new RegExp(text_area_delimiter+'\s*'));
|
40
|
+
}
|
41
|
+
function extractLast(term) {
|
42
|
+
return split(term).pop();
|
43
|
+
}
|
44
|
+
function setTextAreaAutoComplete($el) {
|
45
|
+
$el.autocomplete({
|
46
|
+
source: function(request, response) {
|
47
|
+
$.getJSON($el.attr('data-auto-complete-url'), {
|
48
|
+
term: extractLast(request.term)
|
49
|
+
}, response);
|
50
|
+
},
|
51
|
+
search: function() {
|
52
|
+
// custom minLength
|
53
|
+
var term = extractLast(this.value);
|
54
|
+
if (term.length < 2) {
|
55
|
+
return false;
|
56
|
+
}
|
57
|
+
},
|
58
|
+
focus: function() {
|
59
|
+
// prevent value inserted on focus
|
60
|
+
return false;
|
61
|
+
},
|
62
|
+
select: function(event, ui) {
|
63
|
+
var terms = split( this.value );
|
64
|
+
terms.pop(); // remove the current input
|
65
|
+
terms.push( ui.item.value ); // add the selected item
|
66
|
+
// add placeholder to get the comma-and-space at the end
|
67
|
+
// terms.push("");
|
68
|
+
this.value = terms.join(text_area_delimiter);
|
69
|
+
return false;
|
70
|
+
}
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
data/test/test_helper.rb
ADDED
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: casey_jones
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 217
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 99
|
10
|
+
version: 0.0.99
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tyler Gannon
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-21 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: some extra stuff for rails apps
|
23
|
+
email: t--g__a--nnon@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README
|
30
|
+
files:
|
31
|
+
- Gemfile
|
32
|
+
- README
|
33
|
+
- Rakefile
|
34
|
+
- install.rb
|
35
|
+
- lib/acts_as_linkable/acts_as_linkable.rb
|
36
|
+
- lib/ajax_loading/ajax_loading.rb
|
37
|
+
- lib/ajax_loading/controller_resource.rb
|
38
|
+
- lib/anaf_active_record.rb
|
39
|
+
- lib/casey_jones.rb
|
40
|
+
- lib/casey_jones/active_record_extensions.rb
|
41
|
+
- lib/casey_jones/application_helper_methods.rb
|
42
|
+
- lib/casey_jones/string_reader.rb
|
43
|
+
- lib/generators/anaf_habtm/anaf_habtm_generator.rb
|
44
|
+
- lib/generators/anaf_habtm/assets/nested_attributes.css
|
45
|
+
- lib/generators/anaf_habtm/assets/nested_attributes.js
|
46
|
+
- test/anaf_habtm_test.rb
|
47
|
+
- test/test_helper.rb
|
48
|
+
- uninstall.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/tylergannon/anaf_habtm
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.7
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: My favorite rails goodies all in one
|
83
|
+
test_files:
|
84
|
+
- test/anaf_habtm_test.rb
|
85
|
+
- test/test_helper.rb
|