casey_jones 0.0.99
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.
- 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
|