schofield 0.1.5 → 0.2.0
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/.bundle/config +2 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +28 -0
- data/{LICENSE → LICENSE.txt} +1 -1
- data/README.rdoc +11 -9
- data/Rakefile +28 -24
- data/VERSION +1 -1
- data/lib/generators/schofield/association.rb +94 -0
- data/lib/generators/schofield/attribute.rb +76 -0
- data/lib/generators/schofield/attributes.rb +204 -0
- data/lib/generators/schofield/level.rb +287 -0
- data/lib/generators/schofield/levels.rb +74 -0
- data/lib/generators/schofield/responses.rb +46 -0
- data/lib/generators/schofield/routes.rb +46 -0
- data/lib/generators/schofield/schofield_generator.rb +124 -0
- data/lib/generators/templates/controller.erb +25 -0
- data/lib/generators/templates/form.erb +31 -0
- data/lib/generators/templates/index.erb +5 -0
- data/lib/generators/templates/join_controller.erb +15 -0
- data/lib/generators/templates/model.erb +109 -0
- data/lib/generators/templates/show.erb +21 -0
- data/schofield.gemspec +48 -46
- data/spec/spec_helper.rb +7 -4
- metadata +82 -47
- data/.gitignore +0 -21
- data/generators/schofield_controller/schofield_controller_generator.rb +0 -157
- data/generators/schofield_controller/templates/controller_spec.rb +0 -25
- data/generators/schofield_controller/templates/helper_spec.rb +0 -11
- data/generators/schofield_controller/templates/index_partial.rb +0 -26
- data/generators/schofield_controller/templates/nested/controller.rb +0 -106
- data/generators/schofield_controller/templates/nested/edit.rb +0 -5
- data/generators/schofield_controller/templates/nested/index.rb +0 -3
- data/generators/schofield_controller/templates/nested/new.rb +0 -1
- data/generators/schofield_controller/templates/nested/show.rb +0 -12
- data/generators/schofield_controller/templates/unnested/controller.rb +0 -94
- data/generators/schofield_controller/templates/unnested/edit.rb +0 -5
- data/generators/schofield_controller/templates/unnested/index.rb +0 -3
- data/generators/schofield_controller/templates/unnested/new.rb +0 -1
- data/generators/schofield_controller/templates/unnested/show.rb +0 -12
- data/generators/schofield_controller/templates/view_spec.rb +0 -12
- data/generators/schofield_form/schofield_form_generator.rb +0 -22
- data/generators/schofield_form/templates/view__form.html.haml +0 -11
- data/lib/schofield.rb +0 -111
- data/lib/schofield/tasks.rb +0 -3
- data/lib/schofield/tasks/schofield.rake +0 -7
data/.bundle/config
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "rspec", "~> 2.1.0"
|
10
|
+
gem "bundler", "~> 1.0.0"
|
11
|
+
gem "jeweler", "~> 1.5.1"
|
12
|
+
gem "rcov", ">= 0"
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.5.1)
|
7
|
+
bundler (~> 1.0.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rake (0.8.7)
|
11
|
+
rcov (0.9.9)
|
12
|
+
rspec (2.1.0)
|
13
|
+
rspec-core (~> 2.1.0)
|
14
|
+
rspec-expectations (~> 2.1.0)
|
15
|
+
rspec-mocks (~> 2.1.0)
|
16
|
+
rspec-core (2.1.0)
|
17
|
+
rspec-expectations (2.1.0)
|
18
|
+
diff-lcs (~> 1.1.2)
|
19
|
+
rspec-mocks (2.1.0)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
bundler (~> 1.0.0)
|
26
|
+
jeweler (~> 1.5.1)
|
27
|
+
rcov
|
28
|
+
rspec (~> 2.1.0)
|
data/{LICENSE → LICENSE.txt}
RENAMED
data/README.rdoc
CHANGED
@@ -2,16 +2,18 @@
|
|
2
2
|
|
3
3
|
Description goes here.
|
4
4
|
|
5
|
-
==
|
5
|
+
== Contributing to schofield
|
6
6
|
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
10
|
-
|
11
|
-
* Commit
|
12
|
-
|
13
|
-
*
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
9
|
+
* Fork the project
|
10
|
+
* Start a feature/bugfix branch
|
11
|
+
* Commit and push until you are happy with your contribution
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
14
|
|
15
15
|
== Copyright
|
16
16
|
|
17
|
-
Copyright (c)
|
17
|
+
Copyright (c) 2010 Marc Tauber. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
CHANGED
@@ -1,38 +1,42 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
2
10
|
require 'rake'
|
3
11
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
rescue LoadError
|
19
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "schofield"
|
16
|
+
gem.homepage = "http://github.com/sauberia/schofield"
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.summary = %Q{one-line summary of your gem}
|
19
|
+
gem.description = %Q{longer description of your gem}
|
20
|
+
gem.email = "marc@marctauber.com"
|
21
|
+
gem.authors = ["Marc Tauber"]
|
22
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
25
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
20
26
|
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
21
28
|
|
22
|
-
require '
|
23
|
-
|
24
|
-
|
25
|
-
spec.
|
29
|
+
require 'rspec/core'
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
32
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
26
33
|
end
|
27
34
|
|
28
|
-
|
29
|
-
spec.libs << 'lib' << 'spec'
|
35
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
30
36
|
spec.pattern = 'spec/**/*_spec.rb'
|
31
37
|
spec.rcov = true
|
32
38
|
end
|
33
39
|
|
34
|
-
task :spec => :check_dependencies
|
35
|
-
|
36
40
|
task :default => :spec
|
37
41
|
|
38
42
|
require 'rake/rdoctask'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Schofield
|
2
|
+
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class Association
|
6
|
+
|
7
|
+
ONE_TO_ONE = '1-1'
|
8
|
+
ONE_TO_MANY = '1-*'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
class << self; attr_accessor :all end
|
13
|
+
@all = []
|
14
|
+
|
15
|
+
def self.reference_parents
|
16
|
+
all.each { |a| a.reference_parent }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.report
|
20
|
+
all.each { |a| Responses.say a.report }
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
attr_reader :parent, :child, :parent_name, :child_name , :polymorphic_name
|
26
|
+
attr_writer :nest
|
27
|
+
attr_accessor :cardinality
|
28
|
+
|
29
|
+
def initialize child, parent_name, one_to_one, polymorphic_name=nil
|
30
|
+
@child = child
|
31
|
+
@child_name = child.name
|
32
|
+
@parent_name = parent_name
|
33
|
+
@polymorphic_name = polymorphic_name
|
34
|
+
@cardinality = one_to_one ? ONE_TO_ONE : ONE_TO_MANY
|
35
|
+
@nest = polymorphic_name.present? && !one_to_one
|
36
|
+
self.class.all << self
|
37
|
+
end
|
38
|
+
|
39
|
+
def nest?
|
40
|
+
@nest
|
41
|
+
end
|
42
|
+
|
43
|
+
def reference_parent
|
44
|
+
@parent = Levels.find(@parent_name)
|
45
|
+
@parent.child_associations << self
|
46
|
+
end
|
47
|
+
|
48
|
+
def report
|
49
|
+
<<-STRING.gsub(/^ {8}/, '')
|
50
|
+
parent: #{@parent.name}
|
51
|
+
child: #{@child.name}
|
52
|
+
nest: #{@nest ? 'yes' : 'no'}
|
53
|
+
cardinality: #{@cardinality}
|
54
|
+
polymorphic_name: #{@polymorphic_name}
|
55
|
+
---
|
56
|
+
STRING
|
57
|
+
end
|
58
|
+
|
59
|
+
def one_to_one?
|
60
|
+
@cardinality == Association::ONE_TO_ONE
|
61
|
+
end
|
62
|
+
|
63
|
+
def one_to_many?
|
64
|
+
@cardinality == Association::ONE_TO_MANY
|
65
|
+
end
|
66
|
+
|
67
|
+
def polymorphic?
|
68
|
+
@polymorphic_name.present?
|
69
|
+
end
|
70
|
+
|
71
|
+
def non_polymorphic?
|
72
|
+
!polymorphic?
|
73
|
+
end
|
74
|
+
|
75
|
+
def polymorphic_one_to_one?
|
76
|
+
polymorphic? && one_to_one?
|
77
|
+
end
|
78
|
+
|
79
|
+
def polymorphic_one_to_many?
|
80
|
+
polymorphic? && one_to_many?
|
81
|
+
end
|
82
|
+
|
83
|
+
def non_polymorphic_one_to_one?
|
84
|
+
non_polymorphic? && one_to_one?
|
85
|
+
end
|
86
|
+
|
87
|
+
def non_polymorphic_one_to_many?
|
88
|
+
non_polymorphic? && one_to_many?
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Schofield
|
2
|
+
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class Attribute
|
6
|
+
|
7
|
+
ATTACHMENT_IMAGE_NAMES = %w( image thumbnail enlargement photo avatar )
|
8
|
+
|
9
|
+
attr_reader :model_name, :name, :max_number, :max_length, :attachment_name, :type
|
10
|
+
|
11
|
+
|
12
|
+
def initialize column
|
13
|
+
|
14
|
+
if (match_data = column.name.match(/(.+)_file_name$/))
|
15
|
+
@attachment_name = match_data[1]
|
16
|
+
@attachment = true
|
17
|
+
@image = @attachment_name =~ /#{ATTACHMENT_IMAGE_NAMES.join('|')}/
|
18
|
+
end
|
19
|
+
|
20
|
+
if (match_data = column.name.match(/^(.+)_id$/))
|
21
|
+
@reference = true
|
22
|
+
@model_name = match_data[1]
|
23
|
+
end
|
24
|
+
|
25
|
+
@name = column.name
|
26
|
+
@required = !column.null
|
27
|
+
@type = column.type
|
28
|
+
@max_length = column.type == :string ? column.limit : nil
|
29
|
+
@max_number = column.type == :integer && !@reference ? 256**column.limit / 2 - 1 : nil
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def reference?
|
34
|
+
@reference == true
|
35
|
+
end
|
36
|
+
|
37
|
+
def attachment?
|
38
|
+
@attachment == true
|
39
|
+
end
|
40
|
+
|
41
|
+
def image?
|
42
|
+
@image == 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def required?
|
46
|
+
@required && !@attachment
|
47
|
+
end
|
48
|
+
|
49
|
+
def required_attachment?
|
50
|
+
@attachment && @required
|
51
|
+
end
|
52
|
+
|
53
|
+
def text?
|
54
|
+
type == :text
|
55
|
+
end
|
56
|
+
|
57
|
+
def boolean?
|
58
|
+
type == :boolean
|
59
|
+
end
|
60
|
+
|
61
|
+
def actual_name
|
62
|
+
@attachment_name || @model_name || @name
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_column
|
66
|
+
case
|
67
|
+
when image? then "'#{actual_name.humanize} ', lambda { |x| image_tag(x.#{@attachment_name}.url(:thumbnail), :alt => '', :class => 'thumbnail') }"
|
68
|
+
when attachment? then "link_to('#{actual_name.humanize}', x.#{@attachment_name}.url)"
|
69
|
+
else "'#{actual_name.humanize}', lambda { |x| x.#{actual_name} }"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module Schofield
|
2
|
+
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class Attributes
|
6
|
+
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@attributes = []
|
12
|
+
@requireds = []
|
13
|
+
@required_one_to_ones = []
|
14
|
+
@attachments = []
|
15
|
+
@required_attachments = []
|
16
|
+
@max_numbers = {}
|
17
|
+
@max_lengths = {}
|
18
|
+
@references = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def new_attribute column, one_to_one
|
23
|
+
|
24
|
+
attribute = Attribute.new column
|
25
|
+
@attributes << attribute
|
26
|
+
|
27
|
+
@references += 1 if attribute.reference?
|
28
|
+
|
29
|
+
if attribute.required? && attribute.name != 'position' && !attribute.boolean?
|
30
|
+
if one_to_one then @required_one_to_ones << attribute.name
|
31
|
+
else @requireds << attribute.name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if attribute.attachment?
|
36
|
+
@attachments << attribute.attachment_name
|
37
|
+
@required_attachments << attribute.attachment_name if attribute.required_attachment?
|
38
|
+
end
|
39
|
+
|
40
|
+
if attribute.max_number
|
41
|
+
@max_numbers[attribute.max_number] ||= []
|
42
|
+
@max_numbers[attribute.max_number] << attribute.name
|
43
|
+
end
|
44
|
+
|
45
|
+
if attribute.max_length
|
46
|
+
@max_lengths[attribute.max_length] ||= []
|
47
|
+
@max_lengths[attribute.max_length] << attribute.name
|
48
|
+
end
|
49
|
+
|
50
|
+
attribute
|
51
|
+
end
|
52
|
+
|
53
|
+
def each
|
54
|
+
@attributes.each { |a| yield a }
|
55
|
+
end
|
56
|
+
|
57
|
+
def join?
|
58
|
+
@references == @attributes.select{ |a| a.name != 'position' }.count && @references == 2
|
59
|
+
end
|
60
|
+
|
61
|
+
def validations?
|
62
|
+
validations.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
def attr_protecteds?
|
66
|
+
attr_protecteds.present?
|
67
|
+
end
|
68
|
+
|
69
|
+
def acts_as_markdowns?
|
70
|
+
acts_as_markdowns.present?
|
71
|
+
end
|
72
|
+
|
73
|
+
def attachments?
|
74
|
+
@attachments.any?
|
75
|
+
end
|
76
|
+
|
77
|
+
def validations
|
78
|
+
@validations ||= get_validations
|
79
|
+
end
|
80
|
+
|
81
|
+
def acts_as_markdowns
|
82
|
+
@acts_as_markdowns ||= (names = @attributes.select(&:text?).map(&:name)).any? ? "acts_as_markdown #{join_names(names)}" : ''
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s_string
|
86
|
+
case
|
87
|
+
when name? then 'name'
|
88
|
+
when title? then 'title'
|
89
|
+
when text? then 'text'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def attached_files
|
94
|
+
@attachments.map { |attachment_name|
|
95
|
+
string = "\n has_attached_file :#{attachment_name}, "
|
96
|
+
indentation = ' ' * (string.length - 1)
|
97
|
+
string += ":path => ':class/:id.:extension',\n"
|
98
|
+
string += indentation + ":storage => :s3,\n"
|
99
|
+
string += indentation + ":s3_credentials => \"\#{Rails.root}/config/s3.yml\",\n"
|
100
|
+
string += indentation + ":url => ':s3_domain_url'"
|
101
|
+
}.join("\n")
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def attr_protecteds
|
109
|
+
@attr_protecteds ||= (@attachments.any? ? 'attr_protected :' + @attachments.map { |attachment_name|
|
110
|
+
self.select { |attribute|
|
111
|
+
(match_data = attribute.name.match(/^#{attachment_name}_(.+)$/)).present? && %w(file_name content_type file_size updated_at).include?(match_data[1])
|
112
|
+
}.map(&:name)
|
113
|
+
}.flatten.join(', :') : '')
|
114
|
+
end
|
115
|
+
|
116
|
+
def names_matching regex
|
117
|
+
self.select { |a| a.name.match(regex) }.map(&:name)
|
118
|
+
end
|
119
|
+
|
120
|
+
def join_names names
|
121
|
+
":#{names.join(', :')}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_validations
|
125
|
+
@validations = []
|
126
|
+
validate_presence
|
127
|
+
validate_presence_unless_nested
|
128
|
+
validate_length
|
129
|
+
validate_numericality
|
130
|
+
validate_email_address
|
131
|
+
validate_phone_number
|
132
|
+
validate_url
|
133
|
+
validate_boolean
|
134
|
+
validate_attachment_presence
|
135
|
+
@validations
|
136
|
+
end
|
137
|
+
|
138
|
+
def validate_presence
|
139
|
+
if (names = @requireds.map { |a| a.gsub(/_id$/, '') }).any?
|
140
|
+
@validations << "validates #{join_names(names)}, :presence => true"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def validate_boolean
|
145
|
+
if (booleans = select { |a| a.boolean? && a.required? }).any?
|
146
|
+
@validations << "validates #{join_names(booleans.map(&:name))}, :boolean => true"
|
147
|
+
end
|
148
|
+
if (booleans = select { |a| a.boolean? && !a.required? }).any?
|
149
|
+
@validations << "validates #{join_names(booleans.map(&:name))}, :boolean => true, :allow_nil => true"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_presence_unless_nested
|
154
|
+
if (names = @required_one_to_ones.map { |a| a.gsub(/_id$/, '') }).any?
|
155
|
+
@validations << "validates #{join_names(names)}, :presence => true, :unless => :nested"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def validate_length
|
160
|
+
@max_lengths.each do |length, names|
|
161
|
+
@validations << "validates #{join_names(names)}, :length => { :maximum => #{length} }, :allow_nil => true"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def validate_numericality
|
166
|
+
@max_numbers.each do |number, names|
|
167
|
+
@validations << "validates #{join_names(names)}, :numericality => { :less_than_or_equal_to => #{number} }, :allow_nil => true"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def validate_email_address
|
172
|
+
if (names = names_matching(/email_address$/)).any?
|
173
|
+
@validations << "validates #{join_names(names)}, :email => true, :allow_nil => true"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def validate_phone_number
|
178
|
+
if (names = names_matching(/phone_number$/)).any?
|
179
|
+
@validations << "validates #{join_names(names)}, :phone_number => true, :allow_nil => true"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def validate_url
|
184
|
+
if (names = names_matching(/url$/)).any?
|
185
|
+
@validations << "validates #{join_names(names)}, :url => true, :allow_nil => true"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def validate_attachment_presence
|
190
|
+
@validations << "validates_attachment_presence #{join_names(@required_attachments)}" if @required_attachments.any?
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def method_missing(method, *args, &block)
|
196
|
+
if match_data = method.to_s.match(/^(.+)\?$/)
|
197
|
+
self.find { |a| a.name == match_data[1] }.present?
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|