├── .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 | 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 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 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 | --------------------------------------------------------------------------------