├── .gitignore
├── .idea
├── .rakeTasks
├── encodings.xml
├── ld.iml
├── misc.xml
├── modules.xml
├── scopes
│ └── scope_settings.xml
├── vcs.xml
└── workspace.xml
├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── README2.md
├── Rakefile
├── bin
├── console
└── setup
├── ld.gemspec
└── lib
├── ld.rb
└── ld
├── document
└── document.rb
├── error
├── hint.rb
└── parameter_error.rb
├── excel
├── excel.rb
└── sheet.rb
├── file
├── dir.rb
├── file.rb
└── files.rb
├── print
└── print.rb
├── project
├── controllers.rb
├── models.rb
├── project.rb
├── routes.rb
├── tables.rb
└── views.rb
└── version.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /Gemfile.lock
4 | /_yardoc/
5 | /coverage/
6 | /doc/
7 | /pkg/
8 | /spec/reports/
9 | /tmp/
10 |
--------------------------------------------------------------------------------
/.idea/.rakeTasks:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/ld.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at chuangye201012@163.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in ld.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Liu Dong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ld
2 |
3 | Practical small tools,
4 | For the sake of efficiency,
5 | The Module is my name abbreviations LD,
6 | Basically has the following Class.
7 |
8 |
9 | ```ruby
10 | module Ld
11 | class File
12 | end
13 | class Excel
14 | end
15 | class Project
16 | end
17 | class Print
18 | end
19 | end
20 | ```
21 |
22 | ## Installation
23 |
24 | Add this line to your application's Gemfile:
25 |
26 | ```ruby
27 | gem 'ld'
28 | ```
29 |
30 | And then execute:
31 |
32 | $ bundle
33 |
34 | Or install it yourself as:
35 |
36 | $ gem install ld
37 |
38 | ## Usage
39 |
40 | 1. Ld::Excel
41 | ```ruby
42 | # write excel
43 | Ld::Excel.create :file_path => 'config/excel_test.xls' do |excel|
44 | excel.write_sheet 'abc' do |sheet|
45 | sheet.set_format({color: :red, font_size: 20, font: '微软雅黑'})
46 | sheet.set_point 'a1'
47 | sheet.set_headings ['A','B','C','D']
48 | sheet.set_rows([
49 | ['1','2','3','4'],
50 | ['2','3','4','5'],
51 | ['3','4','5','6'],
52 | ['4','5','6','7']
53 | ])
54 | end
55 | end
56 |
57 | # read excel
58 | excel = Ld::Excel.open('config/excel_test.xls')
59 | excel.read('abc?a1:b5')
60 | excel.read({sheet: 'abc', scope:'a1:b5'})
61 | excel.read({sheet: 'abc', scope:'a1:b5', exclude:'3'})
62 | excel.read({sheet: 'abc', scope:'a1:b5', exclude:'B'})
63 | ```
64 |
65 |
66 | 2. Ld::Project
67 | ```ruby
68 | # Check the project details
69 | project = Ld::Project.new(Rails.root.to_s)
70 |
71 | # create excel to 'config/project_details.xls'
72 |
73 | # Check model infos
74 | project.to_xls('config/project_details.xls')
75 | project.print :user, :fields
76 | project.print :user, :relations
77 | project.print :user, :routes
78 | project.print :user, :controllers
79 | project.print :user, :views
80 |
81 | ```
82 |
83 |
84 | 3. Ld::File
85 | ```ruby
86 | # read file all lines
87 | file = Ld::File.open('config/application.rb')
88 | lines = file.lines
89 |
90 | # read dir
91 | dir = Ld::File.open('app/models')
92 | files = dir.children
93 |
94 | # search dir file by file name
95 | files = dir.search_files(/.rb$/)
96 |
97 | # Ld::File API
98 | Ld::File.open path
99 | Ld::File.new path
100 | file.children
101 | file.brothers
102 | file.father
103 | file.lines
104 | file.search_files(//)
105 | file.search_dirs(//)
106 | file.name
107 | file.path
108 | file.type # 0=file, 1=dir
109 | ```
110 |
111 |
112 | 4. Ld::Print
113 | ```ruby
114 | users = User.first(10)
115 | Ld::Print.p users, 'id ,name, created_at'
116 | ```
117 |
118 |
119 | ## API
120 |
121 |
122 | ### Ld::Project
123 | * `initialize table_hash = {}, project_root_path = Rails.root.to_s`
124 | * 作用:解析一个项目的代码获得结构化的数据
125 |
126 | * `print model_name, type = :relations`
127 | * 作用:查看模型的相关信息(参数有:relations,fields,tables,routes,views,controllers)
128 |
129 | * `to_xls path = {:file_path => "#{@root.path}/project.xls"}`
130 | * 作用:将这个项目的代码分析结果保存到excel文件(默认在项目根目录下的project.xls)
131 |
132 | ### Ld::Excel
133 | * `self.open path`
134 | * 作用:打开一个xls文件,返回Ld::Excel实例
135 |
136 | * `self.write path, &block`
137 | * 作用:写excel(创建新的xls文件)
138 |
139 | * `self.create path, &block`
140 | * 作用:write的同名方法
141 |
142 | * `read params, show_location = false`
143 | * 示例:Ld::Excel.read "Sheet1?A1:B2"
144 | * 作用:读xls文件中的内容,二维数组
145 |
146 | * `read_with_location params`
147 | * 作用:与read方法相同(但会多返回坐标数据)
148 |
149 | ### Ld::Sheet
150 | * `set_headings headings`
151 | * 作用:在当前sheet的主体内容顶上方添加一个表头(传入二维数组),但不写入(只有调用Ld::Excel的实例方法save才会写入io)
152 |
153 | * `set_color color`
154 | * 作用:设置当前sheet页的字体颜色
155 |
156 | * `set_font_size size`
157 | * 作用:设置当前sheet页的字体大小
158 |
159 | * `set_font font`
160 | * 作用:设置当前sheet页的字体
161 |
162 | * `set_weight weight`
163 | * 作用:设置当前sheet页的单元格宽度(暂时无效)
164 |
165 | * `set_point point`
166 | * 作用:设置当前sheet页的字体颜色
167 |
168 | ### Ld::File
169 | * `self.open path`
170 | * 作用:打开一个文件
171 |
172 | * `children `
173 | * 作用:返回这个目录下的所有一级目录与一级文件,如果不是目录,会报错
174 |
175 | * `self.current `
176 | * 作用:返回当前所在目录(Dir.pwd)
177 |
178 | * `dir? `
179 | * 作用:判断这是目录吗
180 |
181 | * `file? `
182 | * 作用:判断这是文件吗
183 |
184 | * `find name`
185 | * 作用:查找文件或目录,返回一个一级目录或文件,如果不存在则返回nil
186 |
187 | * `search name, type = :all`
188 | * 作用:精确查找,返回所有匹配的目录和文件
189 |
190 | * `search_regexp regexp, type = :all`
191 | * 作用:模糊查找,返回所有匹配的目录和文件
192 |
193 | * `lines `
194 | * 作用:如果是一个文本文件,返回所有行
195 |
196 | * `rename new_name`
197 | * 作用:修改名称(目录或文件均可)
198 |
199 | * `delete `
200 | * 作用:删除当前文件(有gets确认)
201 |
202 | * `files `
203 | * 作用:返回所有文件
204 |
205 | * `parent `
206 | * 作用:返回父目录
207 |
208 | * `siblings `
209 | * 作用:返回所有兄弟
210 |
211 | * `dirs `
212 | * 作用:返回所有目录
213 |
214 | * `ls `
215 | * 作用:输出目录中所有条目
216 |
--------------------------------------------------------------------------------
/README2.md:
--------------------------------------------------------------------------------
1 | ## API
2 |
3 |
4 | ### Ld::Project
5 | * `initialize table_hash = {}, project_root_path = Rails.root.to_s`
6 | * 作用:解析一个项目的代码获得结构化的数据
7 |
8 | * `print model_name, type = :relations`
9 | * 作用:查看模型的相关信息(参数有:relations,fields,tables,routes,views,controllers)
10 |
11 | * `to_xls path = {:file_path => "#{@root.path}/project.xls"}`
12 | * 作用:将这个项目的代码分析结果保存到excel文件(默认在项目根目录下的project.xls)
13 |
14 | ### Ld::Excel
15 | * `self.open path`
16 | * 作用:打开一个xls文件,返回Ld::Excel实例
17 |
18 | * `self.write path, &block`
19 | * 作用:写excel(创建新的xls文件)
20 |
21 | * `self.create path, &block`
22 | * 作用:write的同名方法
23 |
24 | * `read params, show_location = false`
25 | * 示例:Ld::Excel.read "Sheet1?A1:B2"
26 | * 作用:读xls文件中的内容,二维数组
27 |
28 | * `read_with_location params`
29 | * 作用:与read方法相同(但会多返回坐标数据)
30 |
31 | ### Ld::Sheet
32 | * `set_headings headings`
33 | * 作用:在当前sheet的主体内容顶上方添加一个表头(传入二维数组),但不写入(只有调用Ld::Excel的实例方法save才会写入io)
34 |
35 | * `set_color color`
36 | * 作用:设置当前sheet页的字体颜色
37 |
38 | * `set_font_size size`
39 | * 作用:设置当前sheet页的字体大小
40 |
41 | * `set_font font`
42 | * 作用:设置当前sheet页的字体
43 |
44 | * `set_weight weight`
45 | * 作用:设置当前sheet页的单元格宽度(暂时无效)
46 |
47 | * `set_point point`
48 | * 作用:设置当前sheet页的字体颜色
49 |
50 | ### Ld::File
51 | * `self.open path`
52 | * 作用:打开一个文件
53 |
54 | * `children `
55 | * 作用:返回这个目录下的所有一级目录与一级文件,如果不是目录,会报错
56 |
57 | * `self.current `
58 | * 作用:返回当前所在目录(Dir.pwd)
59 |
60 | * `dir? `
61 | * 作用:判断这是目录吗
62 |
63 | * `file? `
64 | * 作用:判断这是文件吗
65 |
66 | * `find name`
67 | * 作用:查找文件或目录,返回一个一级目录或文件,如果不存在则返回nil
68 |
69 | * `search name, type = :all`
70 | * 作用:精确查找,返回所有匹配的目录和文件
71 |
72 | * `search_regexp regexp, type = :all`
73 | * 作用:模糊查找,返回所有匹配的目录和文件
74 |
75 | * `lines `
76 | * 作用:如果是一个文本文件,返回所有行
77 |
78 | * `rename new_name`
79 | * 作用:修改名称(目录或文件均可)
80 |
81 | * `delete `
82 | * 作用:删除当前文件(有gets确认)
83 |
84 | * `files `
85 | * 作用:返回所有文件
86 |
87 | * `parent `
88 | * 作用:返回父目录
89 |
90 | * `siblings `
91 | * 作用:返回所有兄弟
92 |
93 | * `dirs `
94 | * 作用:返回所有目录
95 |
96 | * `ls `
97 | * 作用:输出目录中所有条目
98 |
99 | ### Ld::Print
100 | * `self.p models, fields`
101 | * 作用:格式化打印模型数组
102 |
103 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | task :default => :spec
3 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/setup"
4 | require "ld"
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 |
13 | require "irb"
14 | IRB.start(__FILE__)
15 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/ld.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'ld/version'
5 |
6 | Gem::Specification.new do |spec|
7 | spec.name = "ld"
8 | spec.version = Ld::VERSION
9 | spec.authors = ["Liu Dong"]
10 | spec.email = ["chuangye201012@163.com"]
11 |
12 | spec.summary = %q{Practical small tools.}
13 | spec.description = %q{For the sake of efficiency, The Module is my name abbreviations LD, Basically has the following Class.}
14 | spec.homepage = "https://github.com/18810625123/ld"
15 | spec.license = "MIT"
16 |
17 | spec.add_dependency 'terminal-table', '~> 1.8'
18 | spec.add_dependency 'spreadsheet'
19 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20 | # to allow pushing to a single host or delete this section to allow pushing to any host.
21 | if spec.respond_to?(:metadata)
22 | spec.metadata['allowed_push_host'] = "https://rubygems.org"
23 | else
24 | raise "RubyGems 2.0 or newer is required to protect against " \
25 | "public gem pushes."
26 | end
27 |
28 | spec.files = `git ls-files -z`.split("\x0").reject do |f|
29 | f.match(%r{^(test|spec|features)/})
30 | end
31 | spec.bindir = "exe"
32 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33 | spec.require_paths = ["lib"]
34 |
35 | spec.add_development_dependency "bundler", "~> 1.14"
36 | spec.add_development_dependency "rake", "~> 10.0"
37 | spec.add_development_dependency "hpricot", "~> 0.8.6"
38 | end
39 |
--------------------------------------------------------------------------------
/lib/ld.rb:
--------------------------------------------------------------------------------
1 | require "ld/version"
2 |
3 | module Ld
4 | def self.get_ip request
5 | request.env['HTTP_X_FORWARDED_FOR'].present? ? request.env['HTTP_X_FORWARDED_FOR'] : request.remote_ip
6 | end
7 | end
8 |
9 | require "ld/file/file"
10 | require "ld/file/dir"
11 | require "ld/file/files"
12 |
13 | require "ld/project/project"
14 | require "ld/project/routes"
15 | require "ld/project/tables"
16 | require "ld/project/models"
17 | require "ld/project/controllers"
18 | require "ld/project/views"
19 |
20 | require "ld/excel/excel"
21 | require "ld/excel/sheet"
22 |
23 | require "ld/print/print"
24 |
25 | require "ld/document/document"
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/lib/ld/document/document.rb:
--------------------------------------------------------------------------------
1 | class Ld::Document
2 |
3 | attr_accessor :doc
4 |
5 | # 作用 读一个rb文件生成api数据
6 | def initialize file
7 | @doc = {}
8 | @lines = file.lines
9 | @lines.each_with_index do |line, i|
10 | arr = line.split(' ')
11 | if arr.delete_at(0) == 'def'
12 | notes = get_notes(@lines, i)
13 | if notes.size > 0
14 | method = arr.delete_at(0)
15 | @doc[method] = {
16 | params:arr.join(' '),
17 | notes:notes
18 | }
19 | end
20 | end
21 | end
22 | end
23 |
24 | def get_notes lines, i
25 | notes = []
26 | (i-1).downto(0) do |j|
27 | arr = lines[j].split(' ')
28 | if arr[0] == '#='
29 | notes << {title:arr[1], note:arr[2..(arr.size)].join(' ')}
30 | else
31 | return notes
32 | end
33 | end
34 | notes
35 | end
36 |
37 |
38 | def class_name
39 | @lines.each do |line|
40 | if line.split(' ')[0] =='class'
41 | return line.split(' ')[1]
42 | end
43 | end
44 | return nil
45 | end
46 |
47 | def self.write_readme readme_path = '/Users/liudong/ruby/my_gems/ld/README2.md'
48 | docs = Ld::File.open('/Users/liudong/ruby/my_gems/ld/lib/ld').search_regexp(/.rb$/).map{|f| Ld::Document.new f}
49 | arr = ["## API\n\n\n"]
50 | docs.each do |doc|
51 | if !doc.doc.empty?
52 | arr << "### #{doc.class_name}"
53 | # arr << "```ruby"
54 | doc.doc.each do |k, v|
55 | arr << "* `#{k} #{v[:params]}`"
56 | v[:notes].each do |note|
57 | arr << " * #{note[:title]}:#{note[:note]}"
58 | end
59 | arr << ""
60 | end
61 | # arr << "```"
62 | end
63 | end
64 | File.open readme_path,'w' do |file|
65 | arr.each do |line|
66 | file.puts line
67 | end
68 | file.close
69 | end
70 | end
71 |
72 | end
73 |
74 |
--------------------------------------------------------------------------------
/lib/ld/error/hint.rb:
--------------------------------------------------------------------------------
1 | class Ld::Hint
2 |
3 | def initialize hash
4 |
5 | end
6 |
7 |
8 | end
--------------------------------------------------------------------------------
/lib/ld/error/parameter_error.rb:
--------------------------------------------------------------------------------
1 | class Ld::ParameterError
2 | attr_accessor :clazz, :hint
3 |
4 | def initialize clazz, hint
5 | @clazz = clazz
6 | @hint = hint
7 | end
8 |
9 | def self.create clazz, &block
10 | runtime_error = self.new clazz
11 | block.call runtime_error
12 | runtime_error
13 | end
14 |
15 | def add_run
16 |
17 | end
18 |
19 | def raise hash
20 | lines = []
21 | case hash[:error_type]
22 | when :example
23 | # lines =
24 | when :scope
25 | lines = [
26 | "scope参数说明:",
27 | " 1 不能为空",
28 | " 2 格式'单元格1:单元格2',如'A1:B2'代表读取以A1与B2为对角的矩形范围中的所有内容",
29 | ]
30 | end
31 |
32 | lines.each{|line| puts line}
33 |
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/lib/ld/excel/excel.rb:
--------------------------------------------------------------------------------
1 | require 'spreadsheet'
2 | Spreadsheet.client_encoding = 'UTF-8'
3 |
4 | class Ld::Excel
5 | attr_accessor :excel, :path
6 |
7 | def initialize path = nil
8 | if path
9 | if path.match(/.xls$/)
10 | if File::exist? path
11 | @excel = Spreadsheet.open path
12 | @path = path
13 | else
14 | raise "File does not exist: #{path}"
15 | end
16 | else
17 | raise "Can only read .xls!"
18 | end
19 | else
20 | @excel = Spreadsheet::Workbook.new
21 | end
22 | end
23 |
24 | #= 作用 打开一个xls文件,返回Ld::Excel实例
25 | def self.open path
26 | self.new path
27 | end
28 |
29 | #= 作用 写excel(创建新的xls文件)
30 | def self.write path, &block
31 | if path.class == Hash
32 | path = path[:file_path]
33 | end
34 | excel = Ld::Excel.new
35 | block.call excel
36 | excel.save path
37 | end
38 |
39 | #= 作用 write的同名方法
40 | def self.create path, &block
41 | self.write path, &block
42 | end
43 |
44 | #= 作用 读xls文件中的内容,二维数组
45 | #= 示例 Ld::Excel.read "Sheet1?A1:B2"
46 | def read params, show_location = false
47 | case params.class.to_s
48 | when 'String'
49 | shett_name, scope = params.split('?')
50 | @current_sheet = open_sheet shett_name
51 | @current_sheet.read scope, show_location
52 | when 'Hash'
53 | raise "Parameter error! \nnot find 'sheet'" if params[:sheet].nil?
54 | raise "Parameter error! \nnot find 'scope'" if params[:scope].nil?
55 | params[:location] = false if params[:location].nil?
56 | @current_sheet = open_sheet params[:sheet]
57 | @current_sheet.read params, params[:location]
58 | end
59 | end
60 |
61 | #= 作用 与read方法相同(但会多返回坐标数据)
62 | def read_with_location params
63 | read params, true
64 | end
65 |
66 | # 作用 如果xls文件内容有改变,可以刷新(会重新open一次,但这个方法不需要再传入参数了)
67 | def flush
68 | @excel = Ld::Excel.open @path
69 | end
70 |
71 | # 作用 保存(真正执行io写入操作)
72 | def save path
73 | puts "Covers a file: #{path}" if File.exist? path
74 | @excel.write path
75 | puts "Excel save success!"
76 | self
77 | rescue
78 | puts $!
79 | puts $@
80 | false
81 | end
82 |
83 | def new_sheet name
84 | Ld::Sheet.new @excel, name
85 | end
86 |
87 | def open_sheet name
88 | Ld::Sheet.open @excel, name
89 | end
90 |
91 | def write_sheet sheet_name, &block
92 | sheet = new_sheet sheet_name
93 | block.call sheet
94 | sheet.save
95 | true
96 | rescue
97 | puts $!
98 | puts $@
99 | false
100 | end
101 |
102 | end
103 |
104 |
--------------------------------------------------------------------------------
/lib/ld/excel/sheet.rb:
--------------------------------------------------------------------------------
1 | class Ld::Sheet
2 | attr_accessor :excel, :sheet
3 |
4 | ABSCISSA = {}
5 | if ABSCISSA.empty?
6 | zm = 'A'
7 | ABSCISSA[zm] = 0
8 | 19999.times{|i| ABSCISSA[zm.succ!] = i+1}
9 | end
10 |
11 | def initialize excel, name, type = 'new'
12 | raise "sheet name is nil" if !name
13 | @excel = excel
14 | @name = name
15 | case type
16 | when 'new'
17 | @sheet = excel.create_worksheet :name => name
18 | @point = 'a1'
19 | @headings = nil
20 | @rows = []
21 | when 'open'
22 | @sheet = excel.worksheet name
23 | raise "sheet '#{name}' not found!" if !@sheet
24 | end
25 | @format = @sheet.default_format
26 | end
27 |
28 | # 作用 读sheet页数据,返回二维数组
29 | def read scope, show_location = false
30 | raise "scope params is nil" if !scope
31 | map = read_scope_to_map scope
32 | read_arrs map, show_location
33 | end
34 |
35 | # 作用 读取一行
36 | def read_row i
37 | @sheet.row(i-1).to_a
38 | end
39 |
40 | # 作用 解析范围参数
41 | def parse_string_scope scope
42 | PARAMETER_ERROR.hint_and_raise :scope, "'+' or '-' 只能存在1个" if scope.split('+').size > 2 or scope.split('-').size > 2
43 | hash = {}
44 | scope.upcase!
45 | if scope.include? '+'
46 | hash[:scope], other = scope.split('+')
47 | if other.include? '-'
48 | hash[:insert], hash[:delete] = other.split('-')
49 | else
50 | hash[:insert] = other
51 | end
52 | else
53 | if scope.include? '-'
54 | hash[:scope], hash[:delete] = scope.split('-')
55 | else
56 | hash[:scope] = scope
57 | end
58 | end
59 | hash
60 | end
61 |
62 | # 作用 使用范围参数构建maps(预读)
63 | def read_scope_to_map scope
64 | scope = parse_string_scope scope if scope.class == String
65 | PARAMETER_ERROR.hint_and_raise :scope, "缺少scope参数,或':',或':'存在多个" if !scope[:scope] or !scope[:scope].match(/:/) or scope[:scope].split(':').size > 2
66 | a, b = scope[:scope].split(':').map{|point| parse_point point}
67 | cols = (a[:character]..b[:character]).to_a
68 | rows = (a[:number]..b[:number]).to_a
69 | insert_maps rows, cols, scope[:insert].upcase if scope[:insert]
70 | delete_maps rows, cols, scope[:delete].upcase if scope[:delete]
71 |
72 | if scope[:delete]
73 | raise "delete 参数只能是 String" if scope[:delete].class != String
74 | end
75 | rows = rows.uniq.sort
76 | cols = cols.uniq.sort
77 | maps = rows.map do |row|
78 | cols.map do |col|
79 | col_i = ABSCISSA[col]
80 | raise "不存在这个列 \n'#{col}'" if !col_i
81 | {
82 | location:"#{col}#{row}",
83 | row:row - 1,
84 | col:col_i
85 | }
86 | end
87 | end
88 | # 调试
89 | # maps.each do |arr|
90 | # puts arr.map{|a| "#{a[:location]}(#{a[:row]}_#{a[:col]})"}.to_s
91 | # end
92 | maps
93 | end
94 |
95 | # 作用 多读一些行或列
96 | def insert_maps rows, cols, inserts
97 | raise "inserts 参数只能是 String" if inserts.class != String
98 | insert_arr = inserts.split(',').map do |insert|
99 | if insert.match(/:/)
100 | raise "insert params syntax error! \n'#{insert}'" if insert.split(':').size > 2
101 | a, b = insert.split(':')
102 | (a..b).to_a
103 | else
104 | insert
105 | end
106 | end
107 | insert_arr.flatten.each do |insert|
108 | if is_row? insert
109 | rows << insert.to_i
110 | else
111 | cols << insert.upcase
112 | end
113 | end
114 | end
115 |
116 | # 作用 少读一些行或列
117 | def delete_maps rows, cols, deletes
118 | raise "deletes 参数只能是 String" if deletes.class != String
119 | del_arr = deletes.split(',').map do |del|
120 | if del.match(/:/)
121 | raise "del params syntax error! \n'#{del}'" if del.split(':').size > 2
122 | a, b = del.split(':')
123 | (a..b).to_a
124 | else
125 | del
126 | end
127 | end
128 | del_arr.flatten.each do |del|
129 | if is_row? del
130 | rows.delete del.to_i
131 | else
132 | cols.delete del.upcase
133 | end
134 | end
135 | end
136 |
137 | # 作用 读二维数据(使用maps)
138 | def read_arrs map_arrs, show_location
139 | map_arrs.map do |map_arr|
140 | map_arr.map do |map|
141 | value = read_unit_by_xy map[:col], map[:row], true
142 | if show_location
143 | {map[:location] => value}
144 | else
145 | value
146 | end
147 | end
148 | end
149 | end
150 |
151 | # 作用 通过x,y坐标获取一个单元格的内容
152 | def read_unit_by_xy x, y, parse
153 | # puts "x: #{x}\ty: #{y}"
154 | unit = @sheet.row(y)[x]
155 | if unit.instance_of? Spreadsheet::Formula
156 | if parse
157 | return unit.value
158 | end
159 | end
160 | return unit
161 | end
162 |
163 | # 作用 判断要添加或要移除的是一行还是一列
164 | def is_row? row
165 | if row.to_i.to_s == row.to_s
166 | return true
167 | end
168 | false
169 | end
170 |
171 | # 作用 打开一个sheet
172 | def self.open excel, name
173 | self.new excel, name, 'open'
174 | end
175 |
176 | # 作用 创建一个sheet
177 | def self.create excel, name
178 | self.new excel, name, 'new'
179 | end
180 |
181 | # 作用 将数据写入sheet
182 | def save
183 | point = parse_point @point
184 | raise '保存sheet必须要有内容,请 set_rows' if !@rows
185 | raise '保存sheet必须要有name,请 set_rows' if !@name
186 | @rows.unshift @headings if @headings
187 | @sheet.default_format = @format
188 | @rows.each_with_index do |row, r|
189 | row.each_with_index do |unit, c|
190 | row = point[:number] + r - 1
191 | col = ABSCISSA[point[:character]] + c
192 | write_unit_by_xy row, col, unit
193 | end
194 | end
195 | self
196 | end
197 |
198 | # 作用 解析一个字符串坐标(如'A1')返回x,y坐标('A1'返回[0,0])
199 | def parse_point point
200 | raise "无法解析excel坐标,坐标需要是String,不能是#{point.class.to_s}" if point.class != String
201 | point.upcase!
202 | characters = point.scan(/[A-Z]+/)
203 | raise "parse point error! \n'#{point}'" if characters.size != 1
204 | numbers = point.scan(/[0-9]+/)
205 | raise "parse point error! \n'#{point}'" if numbers.size != 1
206 | {:character => characters[0], :number => numbers[0].to_i}
207 | end
208 |
209 | # 作用 在当前sheet中添加主体数据(传入二维数组),但不写入(只有调用Ld::Excel的实例方法save才会写入io)
210 | def set_rows rows
211 | raise '必须是一个数组且是一个二维数组' if rows.class != Array && rows.first.class != Array
212 | @rows = rows
213 | end
214 |
215 | #= 作用 在当前sheet的主体内容顶上方添加一个表头(传入二维数组),但不写入(只有调用Ld::Excel的实例方法save才会写入io)
216 | def set_headings headings
217 | if headings
218 | raise 'headings 必须是一个数组' if headings.class != Array
219 | @headings = headings
220 | else
221 | @headings = nil
222 | end
223 | end
224 |
225 | # 作用 在当前sheet的主体内容末尾添加一行数据(传入一维数组),但不写入(只有调用Ld::Excel的实例方法save才会写入io)
226 | def insert_row row
227 | raise 'insert_row 传入的必须是一个数组' if row.class != Array
228 | @rows << row
229 | end
230 |
231 | # 作用 通过x,y坐标往一个单元格中写入数据,但不写入(只有调用Ld::Excel的实例方法save才会写入io)
232 | def write_unit_by_xy x, y, unit
233 | if unit.class == Array
234 | unit = unit.to_s
235 | puts '提示: 有一个单元格的内容是Array, 它被当成字符串写入'
236 | end
237 | @sheet.row(x)[y] = unit
238 | end
239 |
240 | #= 作用 设置当前sheet页的字体颜色
241 | def set_color color
242 | @format.font.color = color
243 | end
244 |
245 | #= 作用 设置当前sheet页的字体大小
246 | def set_font_size size
247 | raise 'size 必须是一个整数' if size.class != Fixnum
248 | @format.font.size = size
249 | end
250 |
251 | #= 作用 设置当前sheet页的字体
252 | def set_font font
253 | @format.font.name = font
254 | end
255 |
256 | #= 作用 设置当前sheet页的单元格宽度(暂时无效)
257 | def set_weight weight
258 | @format
259 | end
260 |
261 | #= 作用 设置当前sheet页的字体颜色
262 | def set_point point
263 | @point = point
264 | end
265 |
266 | def set_format hash
267 | set_color hash[:color]
268 | set_font_size hash[:font_size]
269 | set_font hash[:font]
270 | end
271 |
272 | end
273 |
274 | =begin
275 |
276 | # ,
297 | @horizontal_align=:center,
298 | @indent_level=0,
299 | @left=:none,
300 | @left_color=:builtin_black,
301 | @number_format="GENERAL",
302 | @pattern=1,
303 | @pattern_bg_color=:border,
304 | @pattern_fg_color=:red,
305 | @regexes=
306 | {:date=>/[YMD]/,
307 | :date_or_time=>/[hmsYMD]/,
308 | :datetime=>/([YMD].*[HS])|([HS].*[YMD])/,
309 | :time=>/[hms]/,
310 | :number=>/([# ]|0+)/,
311 | :locale=>/(?-mix:\A\[\$\-\d+\])/},
312 | =end
313 |
--------------------------------------------------------------------------------
/lib/ld/file/dir.rb:
--------------------------------------------------------------------------------
1 | class Ld::Dir
2 |
3 | attr_accessor :path, :name, :dirs, :files, :others, :all_files, :all_dirs, :all_others
4 | attr_accessor :all_others_sum, :all_files_sum
5 |
6 | def initialize path
7 | @path = path
8 | @name = @path.split('/').last
9 | @my = Ld::File.new @path
10 | get_all
11 | end
12 |
13 | def get_all
14 | @all = @my.search_regexp //, :all
15 | @all_files = []
16 | @all_dirs = []
17 | @all_others = []
18 | @all.each do |a|
19 | case a.type
20 | when 'directory'
21 | @all_dirs << a
22 | when 'file'
23 | @all_files << a
24 | else
25 | @all_others << a
26 | end
27 | end
28 | @all_files_sum = (@all_files.map(&:size).sum.to_f / 1024 / 1024).round(1)
29 | @all_others_sum = (@all_others.map(&:size).sum.to_i / 1024).round(1)
30 | @all_suffix = {}
31 | @all_files.map(&:suffix).uniq.each do |suffix|
32 | @all_suffix[suffix] = get_suffix_count(suffix)
33 | end
34 | nil
35 | end
36 |
37 | def get_suffix_count suffix
38 | count = 0
39 | size = 0
40 | @all_files.each do |f|
41 | if f.suffix == suffix
42 | count += 1
43 | size += f.size
44 | end
45 | end
46 | return [count, (size.to_f / 1024).round(2)]
47 | end
48 |
49 | def details
50 | title = "目录名称:#{path.split('/').last}
51 | 目录:#{@all_dirs.size}(个)
52 | 文件:#{@all_files_sum}(Mb)/#{@all_files.size}(个)
53 | 其它:#{@all_others_sum}(Kb)/#{@all_others.size}(个)"
54 | headings = ['文件类型(后缀)', '数量(个)', '大小(Kb)']
55 | rows = @all_suffix.map{|k,v| [k, v[0], v[1]]}.sort{|b,a| a[1] <=> b[1]}
56 | Ld::Print.print headings:headings,rows:rows,title:title
57 | end
58 |
59 | def search_all_suffix regexp
60 | @all_files.select{|f| f.suffix == regexp}
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/lib/ld/file/file.rb:
--------------------------------------------------------------------------------
1 | class Ld::File
2 |
3 | attr_accessor :path, :name, :suffix, :exist, :size, :mode, :type, :stat
4 | @@current_path = Dir.pwd
5 | @@exclude = ['.', '..']
6 |
7 | def initialize path
8 | @path = path[0] == '/' ? path : "#{Dir.pwd}/#{path}"
9 | @name = File.basename @path
10 | read_attrs
11 | end
12 |
13 | #= 作用 打开一个文件
14 | def self.open path
15 | f = self.new path
16 | f.read_attrs
17 | f
18 | end
19 |
20 | # 编码base64
21 | def self.base64 path
22 | Base64.encode64(File.open(path, 'rb').read)
23 | end
24 |
25 | # 解码base64,并写入图片
26 | def self.write_image_by_base64_and_path base64, path
27 | File.open(path,'wb').write(Base64.decode64 base64)
28 | end
29 |
30 | def read_attrs
31 | @suffix = @name.split('.').last
32 | @suffix = @suffix == @name ? nil : @suffix
33 | if File.exist? @path
34 | @exist = true
35 | @type = File.ftype path
36 | @stat = File.stat path
37 | @size = @stat.size
38 | @mode = @stat.mode
39 | else
40 | @exist = false
41 | @type = 'not found'
42 | end
43 | end
44 |
45 | def look
46 | puts lines
47 | end
48 |
49 | #= 作用 返回这个目录下的所有一级目录与一级文件,如果不是目录,会报错
50 | def children
51 | dir!
52 | Dir.foreach(@path).map{|n| Ld::File.new("#{@path}/#{n}") if !is_exclude? n}.compact.sort{|a,b| a.type <=> b.type}
53 | end
54 |
55 | def is_exclude? name
56 | @@exclude.include? name
57 | end
58 |
59 | #= 作用 返回当前所在目录(Dir.pwd)
60 | def self.current
61 | Ld::File.new @@current_path
62 | end
63 |
64 | def exist!
65 | raise "不存在的 #{path}" if !@exist
66 | end
67 |
68 | #= 作用 判断这是目录吗
69 | def dir?
70 | type == 'directory'
71 | end
72 |
73 | #= 作用 判断这是文件吗
74 | def file?
75 | type == 'file'
76 | end
77 |
78 | def dir!
79 | raise "这不是一个目录,而是一个#{type}:#{path}" if type != 'directory'
80 | end
81 |
82 | def file!
83 | raise "这不是一个文件,而是一个#{type}:#{path}" if type != 'file'
84 | end
85 |
86 | #= 作用 查找文件或目录,返回一个一级目录或文件,如果不存在则返回nil
87 | def find name
88 | dir!
89 | Ld::File.new "#{path}/#{name.to_s}" if File.exist? "#{path}/#{name.to_s}"
90 | end
91 |
92 | #= 作用 精确查找,返回所有匹配的目录和文件
93 | def search name, type = :all
94 | dir!
95 | results = []
96 | iter_search name, results
97 | case type.to_s
98 | when 'all'
99 | results
100 | when 'file'
101 | results.map{|f| f.type == 'file'}
102 | when 'dir'
103 | results.map{|f| f.type == 'directory'}
104 | end
105 | end
106 |
107 | #= 作用 模糊查找,返回所有匹配的目录和文件
108 | def search_regexp regexp, type = :all
109 | dir!
110 | results = []
111 | iter_search_regexp regexp, results
112 | case type.to_s
113 | when 'all'
114 | results
115 | when 'file'
116 | results.map{|f| f.type == 'file'}
117 | when 'dir'
118 | results.map{|f| f.type == 'directory'}
119 | end
120 | end
121 |
122 | def iter_search name, results
123 | children.each do |file|
124 | if file.name == name
125 | results << file
126 | end
127 | if file.dir?
128 | file.iter_search name, results
129 | end
130 | end
131 | end
132 |
133 | def iter_search_regexp regexp, results
134 | children.each do |file|
135 | if file.name.match(regexp)
136 | results << file
137 | end
138 | if file.dir?
139 | file.iter_search_regexp regexp, results
140 | end
141 | end
142 | end
143 |
144 | #= 作用 如果是一个文本文件,返回所有行
145 | def lines
146 | File.open(@path).readlines
147 | end
148 |
149 | # def method_missing name
150 | # find name
151 | # end
152 |
153 | #= 作用 修改名称(目录或文件均可)
154 | def rename new_name
155 | new_path = "#{dir.path}/#{new_name}"
156 | if File.rename @path, new_path
157 | @path = new_path
158 | end
159 | end
160 |
161 | #= 作用 删除当前文件(有gets确认)
162 | def delete
163 | puts "删除!:#{path}\n,确认请输入 delete_file,"
164 | if gets.chomp == 'delete_file'
165 | if File.delete path == 1
166 | @exist = false
167 | puts "删除成功 #{path}"
168 | end
169 | end
170 | end
171 |
172 | #= 作用 返回所有文件
173 | def files
174 | children.select{|f| f.type == 'file'}
175 | end
176 |
177 | #= 作用 返回父目录
178 | def parent
179 | Ld::File.new(File.dirname @path)
180 | end
181 |
182 | #= 作用 返回所有兄弟
183 | def siblings
184 | parent.children
185 | end
186 |
187 | #= 作用 返回所有目录
188 | def dirs
189 | children.select{|f| f.type == 'directory'}
190 | end
191 |
192 | #= 作用 输出目录中所有条目
193 | def ls
194 | if type == 'directory'
195 | Ld::Print.ls self
196 | elsif type == 'file'
197 | Ld::Print.ls self.parent
198 | end
199 | end
200 |
201 | def self.write path, arr
202 | File.open(path)
203 | end
204 |
205 | # test:
206 | def self.test
207 | sdf{100.times{Ld::File.new('app').search_regexp //}}
208 | end
209 |
210 | def sdf &block
211 | t1 = Time.new
212 | block.call
213 | t2 = Time.new
214 | puts t2 - t1
215 | end
216 |
217 | def details
218 | Ld::Dir.new(@path).details
219 | end
220 |
221 | end
222 |
223 |
224 |
225 |
--------------------------------------------------------------------------------
/lib/ld/file/files.rb:
--------------------------------------------------------------------------------
1 | class Ld::Files < Array
2 |
3 | attr_accessor :results
4 |
5 | def initialize files
6 | @results = files
7 | end
8 |
9 | def find name
10 | @results.each do |f|
11 | if f.name == name
12 | return f
13 | end
14 | end
15 | nil
16 | end
17 |
18 | end
--------------------------------------------------------------------------------
/lib/ld/print/print.rb:
--------------------------------------------------------------------------------
1 | require 'terminal-table'
2 |
3 | class Ld::Print
4 |
5 | #= 作用 格式化打印模型数组
6 | def self.p models, fields
7 | t = Terminal::Table.new
8 | t.title = models.first.class.to_s
9 | fields = (fields.class == Array ? fields : fields.split(',')).map{|f| f.rstrip.lstrip}
10 | t.headings = fields
11 | models.map { |model|
12 | fields.map { |field|
13 | value = model.send field
14 | value = value.strftime("%Y/%m/%d %H:%M:%S") if [Date, Time, DateTime, ActiveSupport::TimeWithZone].include? value.class
15 | value
16 | }
17 | }#.sort{|a,b| a[2] <=> b[2]}
18 | .each{|row| t.add_row row}
19 | puts t
20 | end
21 |
22 | def self.print hash
23 | t = Terminal::Table.new
24 | t.title = hash[:title]
25 | t.headings = hash[:headings]
26 | t.rows = hash[:rows]
27 | puts t
28 | end
29 |
30 | def self.ls dir
31 | t = Terminal::Table.new
32 | t.title = "目录列表:#{dir.path}"
33 | t.headings = ["name","type","size","permission"]
34 | t.rows = dir.children.map{|f| [f.name, f.type, f.size, f.mode] if f.name[0] != '.'}.compact.sort{|a,b| a[1] <=> b[1]}
35 | puts t
36 | end
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/lib/ld/project/controllers.rb:
--------------------------------------------------------------------------------
1 | class Ld::Controllers
2 |
3 | attr_accessor :headings, :rows
4 |
5 | def initialize root, models
6 | @root = root
7 | @models = models
8 | parse
9 | end
10 |
11 | def parse
12 | @rows = @root.find('app/controllers').search_regexp(/_controller.rb$/).map { |c|
13 | model_name = c.name.split('_controller')[0].singularize
14 | model_name = @models.models.include?(model_name) ? model_name : nil
15 | lines = c.lines
16 | actions = lines.map{|l| l.split('def ')[1] if l.match(/def /)}.compact
17 | [model_name, c.name,actions.size, lines.size, c.path,actions.join(',')]
18 | }.sort{|a,b| b[2] <=> a[2]}
19 | @headings = ['所属模型名称', '控制器名','action个数', '文件行数','path', '所有action']
20 | end
21 |
22 | def parse_by_model_name model_name
23 | controller = find_controller model_name.pluralize
24 | if controller
25 | controller_lines = controller.lines
26 | controller_methods = controller_lines.map{|l| l.split('def ')[1].chomp if l.match(/def /)}.compact
27 | end
28 | controller
29 | end
30 |
31 | def find_controller model_name
32 | @controllers.each do |c|
33 | if c.name.split('_controller.rb')[0] == model_name
34 | return c
35 | end
36 | end
37 | nil
38 | end
39 |
40 | end
--------------------------------------------------------------------------------
/lib/ld/project/models.rb:
--------------------------------------------------------------------------------
1 | class Ld::Models
2 | attr_accessor :headings, :rows, :models
3 |
4 | def initialize root, tables, table_hash
5 | @root = root
6 | @tables = tables
7 | @table_hash = table_hash
8 | parse
9 | end
10 |
11 | def parse
12 | rows = []
13 | model_files = @root.find('app/models').search_regexp(/.rb$/).map{|m|
14 | m if @tables.tables.include?(m.name.split('.')[0].pluralize)
15 | }.compact
16 | @rows = model_files.map{ |file|
17 | name = file.name.split('.')[0]
18 | lines = file.lines
19 | methods_full = lines.map{|l| l.split('def ')[1] if l.match(/def /)}.compact
20 | methods = methods_full.map{|method_full| method_full.split(' ')[0]}
21 | instance = name.camelize.constantize.new
22 | data_count = nil#name.camelize.constantize.count
23 | fields = instance.attributes.keys
24 | relations = get_relations file.lines
25 | [
26 | name,name.pluralize,@table_hash[name],name.camelize,data_count,lines.size,file.path,methods.size,
27 | methods.join(','),fields.join(','),fields.size,
28 | relations[:has_many].size,relations[:belongs_to].size,relations[:has_one].size,
29 | relations[:has_many].join(",\n"),
30 | relations[:belongs_to].join(",\n"),
31 | relations[:has_one].join(",\n")
32 | ]
33 | }.compact.sort{|a,b| b[0] <=> a[0]} # 按 模型文件行数 排序
34 | @models = @rows.map{|arr| arr[0]}.uniq
35 | @headings = ['模型名','表名','comment','类','数据数量','行数','path', '方法数',
36 | '所有方法', '所有字段','字段数量',
37 | 'has_many个数','belongs_to个数','has_one个数',
38 | 'has_many','belongs_to','has_one']
39 | end
40 |
41 | def get_relations lines
42 | relations = {has_many:[],belongs_to:[],has_one:[]}
43 | lines.each do |line|
44 | arr = line.split(' ')
45 | if ['has_many','belongs_to','has_one'].include? arr[0]
46 | model = arr[1].split(':')[1].split(',')[0]
47 | china = @table_hash[model.singularize]
48 | if !china
49 | if i = arr.index('class_name:')
50 | if class_name = arr[i+1]
51 | class_name = class_name.split('"')[1] if class_name.match(/"/)
52 | class_name = class_name.split("'")[1] if class_name.match(/'/)
53 | china = @table_hash[class_name.underscore]
54 | end
55 | end
56 | end
57 | relations[arr[0].to_sym] << "#{model}(#{china})"
58 | end
59 | end
60 | relations
61 | end
62 |
63 | end
64 |
--------------------------------------------------------------------------------
/lib/ld/project/project.rb:
--------------------------------------------------------------------------------
1 | class Ld::Project
2 |
3 | attr_accessor :root, :tables, :models, :controllers, :views, :routes, :table_hash
4 |
5 | #= 作用 解析一个项目的代码获得结构化的数据
6 | def initialize table_hash = {}, project_root_path = Rails.root.to_s
7 | @root = Ld::File.new project_root_path
8 | @table_hash = table_hash
9 | @schema = @root.find('db/schema.rb')
10 | raise "schema.rb文件不存在\n请运行命令(rake db:schema:dump)或手动添加此文件" if @schema.nil?
11 | raise "schema.rb文件是空的\n请运行命令(rake db:schema:dump)或手动添加此文件" if @schema.lines.size == 0
12 | parse_project
13 | end
14 |
15 | def parse_project
16 | @tables = Ld::Tables.new @root, nil
17 | @models = Ld::Models.new @root, @tables, @table_hash
18 | @routes = Ld::Routes.new @root, @models
19 | @tables = Ld::Tables.new @root, @models
20 | @views = Ld::Views.new @root, @models
21 | @controllers = Ld::Controllers.new @root, @models
22 | end
23 |
24 | #= 作用 查看模型的相关信息(参数有:relations,fields,tables,routes,views,controllers)
25 | def print model_name, type = :relations
26 | model_name = model_name.to_s
27 | if !@models.models.include? model_name
28 | puts "不存在 #{model_name}"
29 | return false
30 | end
31 |
32 | title_str = "#{model_name.camelize}(#{@table_hash[model_name]})"
33 | type = type.to_sym
34 | case type
35 | when :fields
36 | fs = '字段,字段类型,描述,空约束,默认值,精度位数,limit'.split(',')
37 | indexs = fs.map{|f| @tables.headings.index(f)}.compact
38 | rows = []
39 | @tables.rows.select{|a| a[0]==model_name}
40 | .each{|arr| rows << indexs.map{|i| arr[i]} }
41 | puts Terminal::Table.new(
42 | :title => "#{title_str}:字段解释",
43 | :headings => fs,
44 | :rows => rows
45 | )
46 | when :relations
47 | fs = 'has_many,belongs_to,has_one'.split(',')
48 | indexs = fs.map{|f| @models.headings.index(f)}.compact
49 | rows = []
50 | @models.rows.select{|a| a[0]==model_name}.
51 | each{|arr| rows << indexs.map{|i| arr[i]} }
52 | puts Terminal::Table.new(
53 | :title => "#{title_str}:关联关系",
54 | :headings => fs,
55 | :rows => rows
56 | )
57 | when :routes
58 | fs = '控制器,action,请求类型,URI,帮助方法'.split(',')
59 | indexs = fs.map{|f| @routes.headings.index(f)}.compact
60 | rows = []
61 | @routes.rows.select{|a| a[0]==model_name}.
62 | each{|arr| rows << indexs.map{|i| arr[i]} }
63 | puts Terminal::Table.new(
64 | :title => "#{title_str}:路由",
65 | :headings => fs,
66 | :rows => rows
67 | )
68 | when :views
69 | fs = '文件夹名,行数,文件名,path'.split(',')
70 | indexs = fs.map{|f| @views.headings.index(f)}.compact
71 | rows = []
72 | @views.rows.select{|a| a[0]==model_name}.
73 | each{|arr| rows << indexs.map{|i| arr[i]} }
74 | puts Terminal::Table.new(
75 | :title => "#{title_str}:视图",
76 | :headings => fs,
77 | :rows => rows
78 | )
79 | when :controllers
80 | fs = 'action个数,文件行数,所有action'.split(',')
81 | indexs = fs.map{|f| @controllers.headings.index(f)}.compact
82 | rows = []
83 | @controllers.rows.select{|a| a[0]==model_name}.
84 | each{|arr| rows << indexs.map{|i| arr[i]} }
85 | puts Terminal::Table.new(
86 | :title => "#{title_str}:控制器",
87 | :headings => fs,
88 | :rows => rows
89 | )
90 | end
91 |
92 | true
93 | end
94 |
95 | def delete_rows_index
96 | @routes.rows.delete_at 0
97 | @tables.rows.delete_at 0
98 | @models.rows.delete_at 0
99 | @views.rows.delete_at 0
100 | @controllers.rows.delete_at 0
101 | end
102 |
103 | #= 作用 将这个项目的代码分析结果保存到excel文件(默认在项目根目录下的project.xls)
104 | def to_xls path = {:file_path => "#{@root.path}/project.xls"}
105 | Ld::Excel.create path do |excel|
106 | excel.write_sheet 'routes' do |sheet|
107 | sheet.set_format({color: :red, font_size: 14, font: '微软雅黑'})
108 | sheet.set_headings @routes.headings
109 | sheet.set_rows @routes.rows
110 | end
111 | excel.write_sheet 'tables' do |sheet|
112 | sheet.set_headings @tables.headings
113 | sheet.set_rows @tables.rows
114 | end
115 | excel.write_sheet 'models' do |sheet|
116 | sheet.set_headings @models.headings
117 | sheet.set_rows @models.rows
118 | end
119 | excel.write_sheet 'views' do |sheet|
120 | sheet.set_headings @views.headings
121 | sheet.set_rows @views.rows
122 | end
123 | excel.write_sheet 'controllers' do |sheet|
124 | sheet.set_headings @controllers.headings
125 | sheet.set_rows @controllers.rows
126 | end
127 | end
128 | #delete_rows_index
129 | end
130 |
131 | def add_bug
132 |
133 | end
134 |
135 |
136 |
137 | end
--------------------------------------------------------------------------------
/lib/ld/project/routes.rb:
--------------------------------------------------------------------------------
1 | class Ld::Routes
2 |
3 | attr_accessor :headings, :rows
4 |
5 | def initialize root, models
6 | @root = root
7 | @models = models
8 | parse
9 | end
10 |
11 | def parse
12 | system "rake routes > #{@root.path}/routes.txt" if !File.exist? "#{@root.path}/routes.txt"
13 | @rows = @root.find('routes.txt').lines.map{|line|
14 | arr = line.split(' ')
15 | arr.unshift(nil) if arr.size == 3
16 | arr
17 | }.delete_if{|arr| arr.size >= 5 or arr.size <= 2 }.map{|row|
18 | controller, action = row[3].split('#')
19 | controller_name = controller.split('/').last
20 | if @models
21 | @model_name = @models.models.include?(controller_name.singularize) ? controller_name.singularize : nil
22 | end
23 | type = row[1]
24 | help_method = row[0]
25 | uri = row[2]
26 | [@model_name,controller_name, action, type, uri, help_method]
27 | }.sort{|a,b| a[1] <=> b[1]}
28 | File.delete("#{@root.path}/routes.txt") if File.exist? "#{@root.path}/routes.txt"
29 | @headings = ['所属模型','控制器', 'action', '请求类型','URI','帮助方法']
30 | end
31 | end
--------------------------------------------------------------------------------
/lib/ld/project/tables.rb:
--------------------------------------------------------------------------------
1 | class Ld::Tables
2 |
3 | attr_accessor :headings, :rows, :tables
4 |
5 | def initialize root, models
6 | @root = root
7 | @models = models
8 | parse
9 | end
10 |
11 | def parse
12 | tables = {}
13 | read_flag = false
14 | table = ""
15 | @root.find('db/schema.rb').lines.each do |l|
16 | if l.split(' ')[0] == 'end'
17 | read_flag = false
18 | end
19 | if l.split(' ')[0] == 'create_table'
20 | read_flag = true
21 | table = l.split('"')[1]
22 | tables[table] = []
23 | end
24 | if read_flag
25 | tables[table] << l
26 | end
27 | end
28 | rows = []
29 | tables.each do |table_name, lines|
30 | model_class = parse_class table_name
31 | lines.delete_at(0)
32 | lines.each do |line|
33 | hash = parse_line line
34 | if hash[:type] != 'index'
35 | if @models
36 | @model_name = @models.models.include?(table_name.singularize) ? table_name.singularize : nil
37 | end
38 | rows << [@model_name,table_name,hash[:field],hash[:type],hash[:comment],hash[:null],hash[:default],hash[:precision],hash[:limit]]
39 | end
40 | end
41 | end
42 | @headings = ['所属模型','表名','字段','字段类型','描述','空约束','默认值','精度位数','limit']
43 | @rows = rows.sort{|a,b| b[1] <=> a[1]}
44 | @tables = tables.keys
45 | end
46 |
47 | def parse_line line
48 | arr = line.split(' ')
49 | hash = {}
50 | if arr[0].match(/^t./)
51 | hash[:type] = arr[0].split('.')[1]
52 | i = nil
53 | if hash[:type] == 'index'
54 | hash[:name] = arr[i + 1] if i = arr.index('name:')
55 | hash[:unique] = arr[i + 1] if i = arr.index('unique:')
56 | hash[:using] = arr[i + 1] if i = arr.index('using:')
57 | else
58 | hash[:field] = line.split('"')[1]
59 | hash[:null] = arr[i + 1] if i = arr.index('name:')
60 | hash[:limit] = arr[i + 1] if i = arr.index('limit:')
61 | hash[:comment] = arr[i + 1].split('"')[1] if i = arr.index('comment:')
62 | hash[:default] = arr[i + 1] if i = arr.index('default:')
63 | hash[:precision] = arr[i + 1] if i = arr.index('precision:')
64 | hash[:precision] = arr[i + 1] if i = arr.index('precision:')
65 | end
66 | return hash
67 | else
68 |
69 | end
70 | return false
71 | end
72 |
73 | def parse_class table_name
74 | model_class = table_name.singularize.camelize.constantize
75 | return model_class
76 | rescue
77 | return nil
78 | end
79 |
80 |
81 | end
--------------------------------------------------------------------------------
/lib/ld/project/views.rb:
--------------------------------------------------------------------------------
1 | class Ld::Views
2 | attr_accessor :headings, :rows
3 |
4 | def initialize root, models
5 | @root = root
6 | @models = models
7 | parse
8 | end
9 |
10 | def parse
11 | @rows = @root.find('app/views').search_regexp(/.html/).map{|v|
12 | dir_name = v.parent.name
13 | model_name = @models.models.include?(dir_name.singularize) ? dir_name.singularize : nil
14 | [model_name,v.lines.size,dir_name,v.name,v.path]
15 | }.sort{|a,b| b[1] <=> a[1]}
16 | @headings = ['所属模型名','行数','文件夹名','文件名','path']
17 | end
18 |
19 | def find model_name
20 | @view_dirs.each do |view_dir|
21 | if view_dir.name == model_name.pluralize
22 | return Ld::View.new view_dir
23 | end
24 | end
25 | nil
26 | end
27 |
28 | end
29 |
--------------------------------------------------------------------------------
/lib/ld/version.rb:
--------------------------------------------------------------------------------
1 | module Ld
2 | VERSION = "0.4.5"
3 | end
4 |
--------------------------------------------------------------------------------