Repository: Nedomas/zapata Branch: master Commit: 548ea9334e24 Files: 131 Total size: 91.8 KB Directory structure: gitextract_g8mieinx/ ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .travis.yml ├── Appraisals ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin/ │ └── zapata ├── gemfiles/ │ ├── 5.2.gemfile │ └── 6.0.gemfile ├── lib/ │ ├── zapata/ │ │ ├── analyst.rb │ │ ├── cli.rb │ │ ├── core/ │ │ │ ├── collector.rb │ │ │ ├── loader.rb │ │ │ ├── reader.rb │ │ │ └── writer.rb │ │ ├── core.rb │ │ ├── db.rb │ │ ├── diver.rb │ │ ├── predictor/ │ │ │ ├── args.rb │ │ │ ├── chooser.rb │ │ │ └── value.rb │ │ ├── predictor.rb │ │ ├── primitive/ │ │ │ ├── arg.rb │ │ │ ├── array.rb │ │ │ ├── base.rb │ │ │ ├── basic.rb │ │ │ ├── casgn.rb │ │ │ ├── const.rb │ │ │ ├── const_send.rb │ │ │ ├── def.rb │ │ │ ├── defs.rb │ │ │ ├── hash.rb │ │ │ ├── ivar.rb │ │ │ ├── klass.rb │ │ │ ├── lvar.rb │ │ │ ├── missing.rb │ │ │ ├── modul.rb │ │ │ ├── nil.rb │ │ │ ├── optarg.rb │ │ │ ├── raw.rb │ │ │ ├── send.rb │ │ │ ├── sklass.rb │ │ │ └── var.rb │ │ ├── primitive.rb │ │ ├── printer.rb │ │ ├── rzpec/ │ │ │ ├── runner.rb │ │ │ └── writer.rb │ │ └── version.rb │ └── zapata.rb ├── script/ │ ├── bootstrap │ └── test ├── spec/ │ ├── array_spec.rb │ ├── definition_spec.rb │ ├── generation_spec.rb │ ├── hash_spec.rb │ ├── klass_types_spec.rb │ ├── send_spec.rb │ ├── simple_types_spec.rb │ ├── spec_helper.rb │ └── support/ │ └── rails_test_app/ │ ├── README.md │ ├── Rakefile │ ├── app/ │ │ ├── assets/ │ │ │ ├── config/ │ │ │ │ └── manifest.js │ │ │ ├── images/ │ │ │ │ └── .keep │ │ │ ├── javascripts/ │ │ │ │ └── application.js │ │ │ └── stylesheets/ │ │ │ └── application.css │ │ ├── controllers/ │ │ │ ├── application_controller.rb │ │ │ └── concerns/ │ │ │ └── .keep │ │ ├── helpers/ │ │ │ └── application_helper.rb │ │ ├── mailers/ │ │ │ └── .keep │ │ ├── models/ │ │ │ ├── .keep │ │ │ ├── concerns/ │ │ │ │ └── .keep │ │ │ ├── robot_to_test.rb │ │ │ ├── test_array.rb │ │ │ ├── test_const.rb │ │ │ ├── test_definition.rb │ │ │ ├── test_float.rb │ │ │ ├── test_hash.rb │ │ │ ├── test_int.rb │ │ │ ├── test_send.rb │ │ │ ├── test_str.rb │ │ │ ├── test_sym.rb │ │ │ └── testing_module/ │ │ │ ├── bare.rb │ │ │ ├── klass_methods.rb │ │ │ └── nested/ │ │ │ └── inside.rb │ │ └── views/ │ │ └── layouts/ │ │ └── application.html.erb │ ├── config/ │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── database.yml │ │ ├── environment.rb │ │ ├── environments/ │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers/ │ │ │ ├── backtrace_silencers.rb │ │ │ ├── cookies_serializer.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── session_store.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales/ │ │ │ └── en.yml │ │ ├── routes.rb │ │ └── secrets.yml │ ├── config.ru │ ├── db/ │ │ └── seeds.rb │ ├── lib/ │ │ ├── assets/ │ │ │ └── .keep │ │ └── tasks/ │ │ └── .keep │ ├── log/ │ │ └── .keep │ ├── public/ │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ └── robots.txt │ └── spec/ │ ├── models/ │ │ ├── robot_to_test_spec.rb │ │ ├── test_array_spec.rb │ │ ├── test_const_spec.rb │ │ ├── test_definition_spec.rb │ │ ├── test_float_spec.rb │ │ ├── test_hash_spec.rb │ │ ├── test_int_spec.rb │ │ ├── test_send_spec.rb │ │ ├── test_str_spec.rb │ │ ├── test_sym_spec.rb │ │ └── testing_module/ │ │ ├── bare_spec.rb │ │ ├── klass_methods_spec.rb │ │ └── nested/ │ │ └── inside_spec.rb │ ├── rails_helper.rb │ └── spec_helper.rb └── zapata.gemspec ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /spec/support/rails_test_app/Gemfile.lock /tmp/ *.bundle *.so *.o *.a mkmf.log .rspec_status gemfiles/*.lock spec/support/rails_test_app/db/*.sqlite3 spec/support/rails_test_app/db/*.sqlite3-journal spec/support/rails_test_app/log/*.log spec/support/rails_test_app/tmp/ ================================================ FILE: .rspec ================================================ --color --warnings --require spec_helper --pattern "spec/*_spec.rb" ================================================ FILE: .rubocop.yml ================================================ AllCops: TargetRubyVersion: 2.3 Exclude: - 'spec/support/rails_test_app/**/*' Style/Documentation: Enabled: false Layout/ParameterAlignment: Enabled: true EnforcedStyle: with_fixed_indentation Layout/MultilineOperationIndentation: Enabled: true EnforcedStyle: indented Layout/MultilineMethodCallIndentation: Enabled: true EnforcedStyle: indented Layout/CaseIndentation: Enabled: true EnforcedStyle: end Layout/EndAlignment: Enabled: true EnforcedStyleAlignWith: variable Style/EmptyMethod: Enabled: false Metrics/BlockLength: Exclude: - 'spec/**/*_spec.rb' - 'zapata.gemspec' ================================================ FILE: .travis.yml ================================================ language: ruby script: script/test rvm: - 2.3.8 - 2.4.8 - 2.5.7 - 2.6.5 - 2.7.0 gemfile: - gemfiles/5.2.gemfile - gemfiles/6.0.gemfile matrix: exclude: - rvm: 2.3.8 gemfile: gemfiles/6.0.gemfile - rvm: 2.4.8 gemfile: gemfiles/6.0.gemfile ================================================ FILE: Appraisals ================================================ # frozen_string_literal: true appraise '5.2' do gem 'rails', '~> 5.2.0' gem 'sqlite3' end appraise '6.0' do gem 'rails', '~> 6.0.0' gem 'sqlite3' end ================================================ FILE: CONTRIBUTING.md ================================================ ## Collaboration :heart: It is encouraged by somehow managing to bring a cake to your house. I promise, I will really try. This is a great project to understand language architecture in general. A project to let your free and expressionistic side shine through by leaving meta hacks and rainbows everywhere. Thank you to everyone who do. I strongly believe that this can make the developer job less robotic and more creative. 1. [Fork it](https://github.com/Nedomas/zapata/fork) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create a new Pull Request To install, run: ```sh cd zapata script/bootstrap ``` For specs: ```sh script/test # or bundle exec rspec --pattern "spec/*_spec.rb" ``` ================================================ FILE: Gemfile ================================================ # frozen_string_literal: true source 'https://rubygems.org' # Specify your gem's dependencies in zapata.gemspec group :test do gem 'coveralls', require: false end gemspec ================================================ FILE: LICENSE ================================================ Copyright (c) 2014 Domas MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Zapata Who has time to write tests? This is a revolutionary tool to make them write themselves. [][rubygems] [][travis] [][gemnasium] [][coveralls]  # What is your problem? There comes a day where you have this bullshit class ``RobotToTest``. We need tests. :shipit: ```ruby class RobotToTest def initialize(human_name, cv) @name = robot_name(human_name) end def robot_name(human_name) "#{self.class.prefix}_#{human_name}" end def cv { planets: planets } end def nested_fun_objects(fun_objects) 'It was fun' end def self.prefix 'Robot' end private def planets ['Mars', Human.home] end def fun_objects [%i(array in array), { hash: nested_hash }] end def nested_hash { in_hash: { in: array } } end def array %w(array) end end # just another class to analyze class Human def initialize human_name = 'Emiliano' end def self.home 'Earth' end end ``` ## Solving it You run ``zapata generate app/models/robot_to_test.rb`` and pop the champagne. Zapata generated ``spec/models/robot_to_test_spec.rb`` for you. ```ruby describe RobotToTest do let(:robot_to_test) do RobotToTest.new('Emiliano', { planets: ['Mars', Human.home] }) end it '#robot_name' do expect(robot_to_test.robot_name('Emiliano')).to eq('Robot_Emiliano') end it '#cv' do expect(robot_to_test.cv).to eq({ planets: ['Mars', 'Earth'] }) end it '#nested_fun_objects' do expect(robot_to_test.nested_fun_objects([ [:array, :in, :array], { hash: { in_hash: { in: ['array'] } } } ])).to eq('It was fun') end it '#prefix' do expect(RobotToTest.prefix).to eq('Robot') end end ``` ## What does it do? It tries to write a passing RSpec spec off the bat. It does fancy analysis to predict the values it could feed to the API of a class. __To be more specific:__ - Analyzes all vars and methods definitions in ``app/models`` - Checks what arguments does a testable method in ``app/models/robot_to_test.rb`` need - Searches for such variable or method name in methods in analyzed - Selects the most probable value by how many times it was repeated in code - Runs the RSpec and fills in the expected values of the test with those returned by RSpec For more things it can currently do check test files https://github.com/Nedomas/zapata/tree/master/spec ## Workflow with Zapata Say you are writing some new feature on your existing project. Before writing that, you probably want to test out the current functionality. But who has time for that? You let *Zapata* create that quick spec for you. Think of it as a *current functionality lock*. Write more code and when you're happy with the result - lock it up again. ## Requirements - Ruby 2.1+ - Rails 3.0+ ## Installation Add `zapata` to your `Gemfile`. It currently only works if the library is added to the `Gemfile`. ```ruby gem 'zapata', groups: %w(development test) ``` ## Usage To use run ```sh zapata generate app/models/model_name.rb ``` To ignore other files and analyze a single model you want to generate a spec for: ```sh zapata generate app/models/model_name.rb --single ``` ## Collaboration :heart: It is encouraged by somehow managing to bring a cake to your house. I promise, I will really try. This is a great project to understand language architecture in general. A project to let your free and expressionistic side shine through by leaving meta hacks and rainbows everywhere. Thank you to everyone who do. I strongly believe that this can make the developer job less robotic and more creative. 1. [Fork it](https://github.com/Nedomas/zapata/fork) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create a new Pull Request To install, run: ```sh cd zapata script/bootstrap ``` For specs: ```sh script/test # or bundle exec rspec --pattern "spec/*_spec.rb" ``` ## Awareness I am well aware that this is featured in [Ruby Weekly 223](http://rubyweekly.com/issues/223). On that note I'd like to thank everybody who helped it shine through. Special thanks to my comrade [@jpalumickas](https://github.com/jpalumickas), with whom we share a vision of a better world. Also - thank you [@edgibbs](https://github.com/edgibbs), for being the early contributor. [@morron](https://github.com/morron) - for caring. Huge thanks to [@marcinruszkiewicz](https://github.com/marcinruszkiewicz) for reviving this gem to life in 2019/2020. Also additional thanks to [@jpalumickas](https://github.com/jpalumickas) for all the extra fixes and modernizing the codebase. This would not be done without you all. ## Copyright Copyright (c) 2014-2018 Justas, Andrew, Ed, Dmitry, Domas. See [LICENSE](LICENSE) for details. [rubygems]: https://rubygems.org/gems/zapata [travis]: http://travis-ci.org/Nedomas/zapata [gemnasium]: https://gemnasium.com/Nedomas/zapata [coveralls]: https://coveralls.io/r/Nedomas/zapata [codeclimate]: https://codeclimate.com/github/Nedomas/zapata ================================================ FILE: Rakefile ================================================ # frozen_string_literal: true require 'bundler/gem_tasks' ================================================ FILE: bin/zapata ================================================ #!/usr/bin/env ruby # frozen_string_literal: true require 'rubygems' require 'bundler/setup' require 'zapata' require 'zapata/cli' Zapata::CLI.start(ARGV) ================================================ FILE: gemfiles/5.2.gemfile ================================================ # frozen_string_literal: true # This file was generated by Appraisal source 'https://rubygems.org' gem 'rails', '~> 5.2.0' gem 'sqlite3' group :test do gem 'coveralls', require: false end gemspec path: '../' ================================================ FILE: gemfiles/6.0.gemfile ================================================ # frozen_string_literal: true # This file was generated by Appraisal source 'https://rubygems.org' gem 'rails', '~> 6.0.0' gem 'sqlite3' group :test do gem 'coveralls', require: false end gemspec path: '../' ================================================ FILE: lib/zapata/analyst.rb ================================================ # frozen_string_literal: true module Zapata class Analyst attr_reader :result def self.analyze(filename) code = Core::Reader.parse(filename) analyst = Analyst.new(code) result = analyst.result.dup analyst.clean result end def initialize(code) # class dive Diver.search_for(:klass) Diver.dive(code) # var dive Diver.search_for(:var) Diver.dive(code) # def dive Diver.search_for(:def) Diver.dive(code) # send dive Diver.search_for(:send) Diver.dive(code) @result = DB.all end def clean DB.destroy_all end end end ================================================ FILE: lib/zapata/cli.rb ================================================ # frozen_string_literal: true require 'thor' require_relative 'version' module Zapata class CLI < Thor desc 'generate FILENAME', 'Generate spec file for model' option :single, type: :boolean, desc: 'Skip app/models analysis', aliases: :s def generate(filename) Zapata::Revolutionist.generate_with_friendly_output( filename: filename, single: options[:single] ) end desc 'version', 'Shows zapata version' def version puts "v#{Zapata::VERSION}" end end end ================================================ FILE: lib/zapata/core/collector.rb ================================================ # frozen_string_literal: true module Zapata module Core class Collector def self.expand_dirs_to_files(dirs) dirs.map { |dir| Dir["#{dir}/*.rb"] }.flatten end end end end ================================================ FILE: lib/zapata/core/loader.rb ================================================ # frozen_string_literal: true module Zapata module Core class Loader class << self def spec_dir File.join(Dir.pwd, '/spec') end def rails_helper_path File.expand_path("#{spec_dir}/rails_helper", __FILE__) end def spec_helper_path File.expand_path("#{spec_dir}/spec_helper", __FILE__) end def helper_name if File.exist?("#{rails_helper_path}.rb") 'rails_helper' elsif File.exist?("#{spec_helper_path}.rb") 'spec_helper' else raise 'Was not able to load nor rails_helper, nor spec_helper' end end def full_helper_path paths = { rails_helper: rails_helper_path, spec_helper: spec_helper_path }.freeze paths[helper_name.to_sym] end def load_spec_helper $LOAD_PATH << spec_dir require helper_name.to_s end end end end end ================================================ FILE: lib/zapata/core/reader.rb ================================================ # frozen_string_literal: true module Zapata module Core class Reader def self.parse(filename) plain_text_code = File.open(filename).read Parser::CurrentRuby.parse(plain_text_code) end end end end ================================================ FILE: lib/zapata/core/writer.rb ================================================ # frozen_string_literal: true module Zapata module Core class Writer def initialize(filename) @filename = filename @padding = 0 clean end def clean file = File.open(@filename, 'w') file.write('') file.close end def append_line(line = '') @padding -= 1 if word_exists?(line, 'end') padding_to_use = @padding padding_to_use = 0 if line.empty? file = File.open(@filename, 'ab+') file.puts("#{' ' * padding_to_use}#{line}") file.close @padding += 1 if word_exists?(line, 'do') end def word_exists?(string, word) !!/\b(?:#{word})\b/.match(string) end end end end ================================================ FILE: lib/zapata/core.rb ================================================ # frozen_string_literal: true require_relative 'core/collector' require_relative 'core/loader' require_relative 'core/reader' require_relative 'core/writer' module Zapata module Core end end ================================================ FILE: lib/zapata/db.rb ================================================ # frozen_string_literal: true module Zapata class DB @records = [] @locs = [] class << self def create(record) loc = record.code.loc unless @locs.include?(loc) @records << record @locs << loc record end end def all @records end def destroy_all @records = [] end end end class SaveManager def self.clean(name) name.to_s.delete('@').to_sym end end end ================================================ FILE: lib/zapata/diver.rb ================================================ # frozen_string_literal: true module Zapata RETURN_TYPES = %i[missing raw const_send sym float str int ivar true false const nil].freeze FINAL_TYPES = Zapata::RETURN_TYPES + %i[array hash] DIVE_TYPES = %i[args begin block defined? nth_ref splat kwsplat class block_pass sclass masgn or and irange erange when and return array kwbegin yield while dstr ensure pair].freeze ASSIGN_TYPES = %i[ivasgn lvasgn or_asgn casgn optarg].freeze DEF_TYPES = %i[def defs].freeze HARD_TYPES = %i[if dsym resbody mlhs next self break zsuper super retry rescue match_with_lvasgn case op_asgn regopt regexp].freeze TYPES_BY_SEARCH_FOR = { klass: %i[class], var: ASSIGN_TYPES, def: DEF_TYPES, send: %i[send] }.freeze PRIMITIVE_TYPES = { Def: %i[def], Defs: %i[defs], Send: %i[send], Array: %i[args array], Hash: %i[hash], Ivar: %i[ivar], Lvar: %i[lvar], Klass: %i[class], Sklass: %i[sclass], Modul: %i[module], Const: %i[const], Optarg: %i[optarg], Arg: %i[arg], Basic: RETURN_TYPES, Casgn: %i[casgn], Var: ASSIGN_TYPES }.freeze class Diver class << self attr_accessor :current_moduls, :current_klass, :current_sklass, :access_level def search_for(what) @search_for = what @current_moduls ||= [] end def dive(code) return Primitive::Nil.new unless code if HARD_TYPES.include?(code.type) return Primitive::Raw.new(:missing, :hard_type) end if (klass = primitive_klass(code)) result = klass.new(code) DB.create(result) if search_for_types.include?(code.type) end deeper_dives(code) result || Primitive::Raw.new(:super, nil) end def primitive_klass(code) primitive_type = find_primitive_type(code) return unless primitive_type "Zapata::Primitive::#{primitive_type}".constantize end def find_primitive_type(code) klass_pair = PRIMITIVE_TYPES.detect do |_, types| types.include?(code.type) end return unless klass_pair klass_pair.first end def search_for_types TYPES_BY_SEARCH_FOR[@search_for] end def deeper_dives(code) return unless DIVE_TYPES.include?(code.type) code.to_a.compact.each do |part| dive(part) end end end end end ================================================ FILE: lib/zapata/predictor/args.rb ================================================ # frozen_string_literal: true module Zapata module Predictor class Args class << self def literal(args_node) return unless args_node raw_args = Diver.dive(args_node).to_raw chosen_args = choose_values(raw_args) Printer.print(chosen_args, args: true) end def choose_values(raw_args) case raw_args.type when :array array = raw_args.value.map do |arg| Value.new(arg.value, arg).choose.to_raw end Primitive::Raw.new(:array, array) when :hash hash = raw_args.value.each_with_object({}) do |(rkey, rval), obj| key = Value.new(rkey.value, rkey).choose.to_raw val = Value.new(rval.value, rval).choose.to_raw obj[key] = val end Primitive::Raw.new(:hash, hash) when :int Primitive::Raw.new(:int, raw_args.value) when :missing Primitive::Raw.new(:missing, raw_args.value) when :nil Primitive::Nil.new.to_raw else raise 'Not yet implemented' end end end end end end ================================================ FILE: lib/zapata/predictor/chooser.rb ================================================ # frozen_string_literal: true module Zapata module Predictor class Chooser def initialize(possible_values) @possible_values = possible_values.dup end def by_probability return if @possible_values.empty? by_count end private def by_count group_with_counts(@possible_values).max_by { |_, v| v }.first end def group_with_counts(values) values.each_with_object(Hash.new(0)) do |value, obj| obj[value] += 1 end end end end end ================================================ FILE: lib/zapata/predictor/value.rb ================================================ # frozen_string_literal: true module Zapata module Predictor class Value extend Memoist def initialize(name, finder = nil) @name = name @finder = finder end def choose return Primitive::Raw.new(:nil, nil) if @name.nil? return @finder if @finder && FINAL_TYPES.include?(@finder.type) return Primitive::Raw.new(:super, @name) if possible_values.empty? Chooser.new(possible_values).by_probability end def a_finder?(primitive) return false unless @finder primitive.class == @finder.class && primitive.name == @finder.name end def possible_values Revolutionist.analysis_as_array.select do |element| !a_finder?(element) && element.name == @name end end memoize :possible_values end end end ================================================ FILE: lib/zapata/predictor.rb ================================================ # frozen_string_literal: true require_relative 'predictor/args' require_relative 'predictor/chooser' require_relative 'predictor/value' module Zapata module Predictor end end ================================================ FILE: lib/zapata/primitive/arg.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Arg < Base def node name = @code.to_a.first type = @code.type OpenStruct.new(type: type, name: name, body: @code) end def to_raw chosen_value = Predictor::Value.new(node.name, self).choose.to_raw return_with_super_as_missing(chosen_value, self) end end end end ================================================ FILE: lib/zapata/primitive/array.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Array < Base def node body = @code type = @code.type OpenStruct.new(type: type, body: body) end def to_a value end def to_raw value = node.body.to_a.map do |node| primitive = Diver.dive(node) raw = primitive.to_raw if raw.type == :super predicted = Predictor::Value.new(raw.value).choose.to_raw return_with_super_as_missing(predicted, primitive) else raw end end Raw.new(:array, value) end end end end ================================================ FILE: lib/zapata/primitive/base.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Base attr_accessor :code, :type def initialize(code) @code = code @type = code.type end def name SaveManager.clean(node.name) end def dive_deeper return if RETURN_TYPES.include?(node.type) Diver.dive(node.args) Diver.dive(node.body) end def to_raw Diver.dive(node.body).to_raw end def return_with_super_as_missing(raw, primitive) raw.type == :super ? Missing.new(primitive.name).to_raw : raw end def return_with_missing_as_super(raw, name) raw.type == :missing ? Raw.new(:super, name) : raw end end end end ================================================ FILE: lib/zapata/primitive/basic.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Basic < Base def to_a [value] end def node body = @code type = @code.type OpenStruct.new(type: type, body: body) end def to_raw Raw.new(node.body.type, node.body.to_a.last) end end end end ================================================ FILE: lib/zapata/primitive/casgn.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Casgn < Base def node modul, name, body = @code.to_a type = @code.type OpenStruct.new(type: type, modul: modul, name: name, body: body) end def literal Diver.dive(node.body).literal end end end end ================================================ FILE: lib/zapata/primitive/const.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Const < Basic def node modul, klass = @code.to_a type = @code.type OpenStruct.new(modul: modul, klass: klass, type: type) end def to_raw Raw.new(:const, [node.modul, node.klass].compact.join('::')) end end end end ================================================ FILE: lib/zapata/primitive/const_send.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class ConstSend def initialize(raw_receiver, method_name, args) @raw_receiver = raw_receiver @method_name = method_name @args = args end def node OpenStruct.new(method_name: @method_name, args: @args) end def to_raw Raw.new(:const_send, "#{Printer.print(@raw_receiver)}.#{node.method_name}#{Predictor::Args.literal(node.args)}") end end end end ================================================ FILE: lib/zapata/primitive/def.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Def < Base attr_accessor :klass def initialize(code) @code = code @klass = Diver.current_klass @self = Diver.current_sklass @access_level = Diver.access_level dive_deeper end def self? !!@self end def public? @access_level == :public end def node name, args, body = @code.to_a type = @code.type OpenStruct.new(type: type, name: name, args: args, body: body) end def literal_predicted_args Predictor::Args.literal(node.args) end end end end ================================================ FILE: lib/zapata/primitive/defs.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Defs < Base attr_accessor :klass def initialize(code) @code = code @klass = Diver.current_klass @access_level = Diver.access_level dive_deeper end def self? true end def public? @access_level == :public end def node _, name, args, body = @code.to_a type = @code.type OpenStruct.new(type: type, name: name, args: args, body: body) end def literal_predicted_args Predictor::Args.literal(node.args) end end end end ================================================ FILE: lib/zapata/primitive/hash.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Hash < Base def node body = @code type = @code.type OpenStruct.new(type: type, body: body) end def to_a value.to_a.flatten end def to_raw result = {} node.body.to_a.each do |pair| key_node, value_node = pair.to_a key = Diver.dive(key_node).to_raw value = Diver.dive(value_node).to_raw result[key] = value end Raw.new(:hash, result) end end end end ================================================ FILE: lib/zapata/primitive/ivar.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Ivar < Basic end end end ================================================ FILE: lib/zapata/primitive/klass.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Klass < Base def initialize(code) @code = code @moduls = Diver.current_moduls.dup Diver.access_level = :public Diver.current_klass = self dive_deeper Diver.current_klass = nil end def parent_modul_names @moduls.map { |mod| mod&.name }.compact end def node const, _, body = @code.to_a immediate_modul, klass = const.to_a chain = parent_modul_names + [immediate_modul, klass] name = chain.compact.join('::') type = @code.type OpenStruct.new( type: type, immediate_modul: immediate_modul, klass: klass, name: name, body: body ) end end end end ================================================ FILE: lib/zapata/primitive/lvar.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Lvar < Base def node name = @code.to_a.first type = @code.type OpenStruct.new(type: type, name: name, body: @code) end def to_raw chosen_value = Predictor::Value.new(node.name, self).choose if chosen_value.respond_to?(:node) && chosen_value.node.body == node.body Missing.new(node.name).to_raw else chosen_value.to_raw end end end end end ================================================ FILE: lib/zapata/primitive/missing.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Missing def initialize(name) @name = name end def node OpenStruct.new(type: :missing) end def to_raw Raw.new(:missing, @name) end end end end ================================================ FILE: lib/zapata/primitive/modul.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Modul < Base def initialize(code) @code = code Diver.current_moduls << self dive_deeper Diver.current_moduls.pop end def node const, body = @code.to_a modul, name = const.to_a type = @code.type OpenStruct.new(type: type, modul: modul, name: name, body: body) end end end end ================================================ FILE: lib/zapata/primitive/nil.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Nil < Basic def initialize end def node OpenStruct.new(type: :nil) end def to_raw Raw.new(:nil, nil) end end end end ================================================ FILE: lib/zapata/primitive/optarg.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Optarg < Base def node name, body = @code.to_a type = @code.type OpenStruct.new(type: type, name: name, body: body) end def to_raw Diver.dive(node.body).to_raw end end end end ================================================ FILE: lib/zapata/primitive/raw.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Raw attr_accessor :type, :value def initialize(type, value) @type = type @value = value end def to_raw self end end end end ================================================ FILE: lib/zapata/primitive/send.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Send < Base def initialize(code) super if node.name == :private Diver.access_level = :private elsif node.name == :protected Diver.access_level = :protected elsif node.name == :public Diver.access_level = :public end end def to_a [value] end def node receiver, name, args = @code.to_a type = @code.type OpenStruct.new(type: type, name: name, args: args, receiver: receiver) end def raw_receiver return unless node.receiver Diver.dive(node.receiver).to_raw end def to_raw if raw_receiver && raw_receiver.type == :const ConstSend.new(raw_receiver, node.name, node.args).to_raw else missing_name = if node.receiver Unparser.unparse(code) else node.name end Missing.new(missing_name).to_raw end end end end end ================================================ FILE: lib/zapata/primitive/sklass.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Sklass < Base def initialize(code) @code = code Diver.current_sklass = self dive_deeper Diver.current_sklass = nil end def node _, body = @code.to_a type = @code.type OpenStruct.new(type: type, body: body) end end end end ================================================ FILE: lib/zapata/primitive/var.rb ================================================ # frozen_string_literal: true module Zapata module Primitive class Var < Base def node name, body = @code.to_a type = @code.type OpenStruct.new(type: type, name: name, body: body) end def literal Diver.dive(node.body).literal end def to_raw raw = Diver.dive(node.body).to_raw if raw.type == :super Missing.new(node.name).to_raw else raw end end end end end ================================================ FILE: lib/zapata/primitive.rb ================================================ # frozen_string_literal: true require_relative 'primitive/base' require_relative 'primitive/arg' require_relative 'primitive/array' require_relative 'primitive/basic' require_relative 'primitive/casgn' require_relative 'primitive/const' require_relative 'primitive/const_send' require_relative 'primitive/def' require_relative 'primitive/defs' require_relative 'primitive/hash' require_relative 'primitive/ivar' require_relative 'primitive/klass' require_relative 'primitive/lvar' require_relative 'primitive/missing' require_relative 'primitive/modul' require_relative 'primitive/nil' require_relative 'primitive/optarg' require_relative 'primitive/raw' require_relative 'primitive/send' require_relative 'primitive/sklass' require_relative 'primitive/var' module Zapata module Primitive end end ================================================ FILE: lib/zapata/printer.rb ================================================ # frozen_string_literal: true module Zapata class Printer class << self extend Memoist def print(raw, args: false) type = raw.type result = case type when :const, :send, :int, :const_send, :literal, :float raw.value when :str str(raw) when :sym sym(raw) when :true true when :false false when :array array(raw.value) when :hash hash(raw.value) when :nil 'nil' when :missing missing(raw) when :ivar ivar(raw) else raise "Not yet implemented #{raw}" end args ? argize(result, type) : result end def to_var_name(name) name.to_s.split('::').last.underscore.delete('@') end private def array(given) unnested = given.map { |el| unnest(el) } "[#{unnested.join(', ')}]" end def str(raw) # decide which one to use # "\"#{raw.value}\"" "'#{raw.value}'" end def sym(raw) ":#{raw.value}" end def ivar(raw) RZpec::Writer.ivars << raw to_var_name(raw.value) end def missing(raw) print(Primitive::Raw.new(:str, "Missing \"#{raw.value}\"")) end def argize(value, type) case type when :array value = value[1...-1] when :hash value = value[2...-2] end return unless value.present? "(#{[value].flatten.join(', ')})" end def hash(given) unnested = given.each_with_object({}) do |(key, val), obj| obj[unnest(key)] = unnest(val) end values = unnested.map do |key, val| print_hash_pair(key, val, all_keys_symbols?(unnested)) end "{ #{values.join(', ')} }" end def print_hash_pair(key, val, symbol_keys) symbol_keys ? "#{key[1..-1]}: #{val}" : "#{key} => #{val}" end def all_keys_symbols?(hash) hash.keys.all? do |key| Parser::CurrentRuby.parse(key.to_s).type == :sym end end memoize :all_keys_symbols? def unnest(raw) return raw unless raw.respond_to?(:value) if raw.value.is_a?(Primitive::Raw) print(unnest(raw.value)) else print(raw) end end end end end ================================================ FILE: lib/zapata/rzpec/runner.rb ================================================ # frozen_string_literal: true module Zapata module RZpec class Runner attr_reader :ran def initialize(spec_filename) @spec_filename = spec_filename run # silence { run } end def silence original_stderr = $stderr.dup original_stdout = $stdout.dup $stdout.reopen('/dev/null', 'w') $stdout.reopen('/dev/null', 'w') yield $stderr = original_stderr $stdout = original_stdout end def methodz examples.index_by { |ex| ex['description'].delete('#').to_sym } end def result_message(method_name) methodz[method_name]['exception']['message'] end def expected(method_name) report_lines = result_message(method_name).to_s.split(/\n/) expected_line = report_lines.detect { |line| line.match('got:') } if expected_line clean_expected_line = expected_line[10..-1] if (matches = clean_expected_line.match(/\#\<(.+):(.+)\>/)) "'Returned instance object #{matches[1]}'" else Printer.print(Diver.dive(Parser::CurrentRuby.parse(clean_expected_line)).to_raw) end else "'Exception in RSpec'" end end def run @ran = true @stdin, @stdout, @stderr = Bundler.with_clean_env do Open3.popen3("bundle exec rspec #{@spec_filename} --format j") end end def examples parsed_json_result['examples'] end def parsed_json_result @json ||= JSON.parse(@stdout.readlines.last) end end end end ================================================ FILE: lib/zapata/rzpec/writer.rb ================================================ # frozen_string_literal: true module Zapata module RZpec class Writer class << self attr_accessor :ivars def reset_ivars @ivars = [] end end def initialize(file, _code, subject_analysis, whole_analysis, spec_analysis = nil) self.class.reset_ivars @subject_analysis = subject_analysis @whole_analysis = whole_analysis @spec_analysis = spec_analysis @writer = Core::Writer.new(file) @result = {} write_require klasses.each do |klass| write_class(klass) end self.class.reset_ivars end def write_require @writer.append_line("require '#{Core::Loader.helper_name}'") end def klasses @subject_analysis.select { |obj| obj.is_a?(Primitive::Klass) } end def klass_defs(klass) @subject_analysis.select do |method| [Primitive::Def, Primitive::Defs].include?(method.class) && method.public? && method.klass.name == klass.name end end def initialize_def(klass) klass_defs(klass).detect { |method| method.name == :initialize } end def write_class(klass) @writer.append_line @writer.append_line("describe #{klass.name} do") write_instance_let(klass) klass_defs(klass).each do |primitive_def| write_method(primitive_def) end self.class.ivars.each do |ivar| predicted_value = Predictor::Value.new(ivar.value, ivar).choose literal_predicted_value = Printer.print(predicted_value.to_raw) write_let(ivar.value, literal_predicted_value) end @writer.append_line('end') end def write_instance_let(klass) if initialize_def = initialize_def(klass) write_let_from_initialize(initialize_def) else write_let(klass.name, "#{klass.name}.new") end end def write_let(name, block) @writer.append_line("let(:#{Printer.to_var_name(name)}) do") @writer.append_line(block) @writer.append_line('end') end def write_let_from_initialize(initialize_def) block = "#{initialize_def.klass.name}.new#{initialize_def.literal_predicted_args}" write_let(initialize_def.klass.name, block) end def write_method(primitive_def) return unless primitive_def.node.body return if primitive_def.name == :initialize @writer.append_line @writer.append_line("it '##{primitive_def.name}' do") receiver = if primitive_def.self? primitive_def.klass.name else Printer.to_var_name(primitive_def.klass.name) end @writer.append_line( "expect(#{receiver}.#{primitive_def.name}#{primitive_def.literal_predicted_args}).to eq(#{write_equal(primitive_def.name)})" ) @writer.append_line('end') end def write_equal(method_name) if @spec_analysis Printer.print(Primitive::Raw.new(:literal, @spec_analysis.expected(method_name))) else Printer.print(Primitive::Raw.new(:str, 'Fill this in by hand')) end end end end end ================================================ FILE: lib/zapata/version.rb ================================================ # frozen_string_literal: true module Zapata VERSION = '1.0.0' end ================================================ FILE: lib/zapata.rb ================================================ # frozen_string_literal: true require 'parser/current' require 'unparser' require 'tempfile' require 'rails' require 'open3' require 'rspec' require 'memoist' require_relative 'zapata/core' require_relative 'zapata/predictor' require_relative 'zapata/primitive' require_relative 'zapata/rzpec/runner' require_relative 'zapata/rzpec/writer' require_relative 'zapata/analyst' require_relative 'zapata/diver' require_relative 'zapata/db' require_relative 'zapata/printer' require_relative 'zapata/version' module Zapata class Revolutionist class << self attr_accessor :analysis, :analysis_as_array def generate_with_friendly_output(filename:, single: false) spec_filename = Zapata::Revolutionist.generate(filename: filename, single: single) puts "Its done, comrades. File #{spec_filename} was generated." end def generate(filename:, single: false) dirs = single ? [] : %w[app/models] file_list = Core::Collector.expand_dirs_to_files(dirs) new(file_list).generate_rspec_for(filename, spec_filename(filename)) end def init_analysis_as_array @analysis_as_array = analysis.values.flatten end def spec_filename(filename) filename.gsub('app/', 'spec/').gsub('.rb', '_spec.rb') end private def single?(opts, args) opts.single? || args.include?('-s') || args.include?('--single') end end def initialize(file_list) Core::Loader.load_spec_helper self.class.analysis = analyze_multiple(file_list) end def analyze_multiple(files) total = files.size.to_s files.each_with_object({}).with_index do |(filename, obj), i| puts "[#{adjusted_current(i, total)}/#{total}] Analyzing: #{filename}" obj[filename] = Analyst.analyze(filename) end end def adjusted_current(index, total) (index + 1).to_s.rjust(total.size) end def generate_rspec_for(filename, spec_filename) unless self.class.analysis[filename] self.class.analysis[filename] = Analyst.analyze(filename) end self.class.init_analysis_as_array code = Core::Reader.parse(filename) global_analysis = Revolutionist.analysis_as_array # first run Tempfile.open('spec') do |tempfile| RZpec::Writer.new(tempfile.path, code, self.class.analysis[filename], global_analysis) save_spec_file(tempfile.path, spec_filename) spec_analysis = RZpec::Runner.new(spec_filename) # second run with RSpec results RZpec::Writer.new(tempfile.path, code, self.class.analysis[filename], global_analysis, spec_analysis) save_spec_file(tempfile.path, spec_filename) end end def save_spec_file(tmp_spec_filename, spec_filename) spec_path = "#{Dir.pwd}/#{spec_filename}" spec_dir = spec_path.split('/')[0...-1].join('/') FileUtils.mkdir_p(spec_dir) FileUtils.cp(tmp_spec_filename, spec_path) spec_filename end end end ================================================ FILE: script/bootstrap ================================================ #!/usr/bin/env bash set -e rake install cd spec/support/rails_test_app bundle update cd ../../.. echo 'Run specs with:' echo './script/test' ================================================ FILE: script/test ================================================ #!/usr/bin/env bash set -e bundle exec rspec --pattern "spec/*_spec.rb" ================================================ FILE: spec/array_spec.rb ================================================ # frozen_string_literal: true describe Zapata::Revolutionist do before(:all) do @generated = exec_generation('app/models/test_array.rb') end it '#test_in_arg' do has_block('#test_in_arg', %{ expect(test_array.test_in_arg([2, 7.1, 8])).to eq([2, 7.1, 8]) }) end it '#test_nested_one_level' do has_block('#test_nested_one_level', %{ expect(test_array.test_nested_one_level([[2, 7.1, 8], :mexico])).to eq([[2, 7.1, 8], :mexico]) }) end it '#test_nested_two_levels' do has_block('#test_nested_two_levels', %{ expect(test_array.test_nested_two_levels([[[2, 7.1, 8], :mexico], [2, 7.1, 8], :mexico])).to eq([[[2, 7.1, 8], :mexico], [2, 7.1, 8], :mexico]) }) end it '#test_nested_three_levels' do has_block('#test_nested_three_levels', %{ expect(test_array.test_nested_three_levels([[[[2, 7.1, 8], :mexico], [2, 7.1, 8], :mexico], [[2, 7.1, 8], :mexico], [2, 7.1, 8], :mexico])).to eq([[[[2, 7.1, 8], :mexico], [2, 7.1, 8], :mexico], [[2, 7.1, 8], :mexico], [2, 7.1, 8], :mexico]) }) end it '#test_hash_nested' do has_block('#test_hash_nested', %{ expect(test_array.test_hash_nested([{ emiliano: [2, 7.1, 8] }])).to eq([{ emiliano: [2, 7.1, 8] }]) }) end end ================================================ FILE: spec/definition_spec.rb ================================================ # frozen_string_literal: true describe Zapata::Revolutionist do before(:all) do @generated = exec_generation('app/models/test_definition.rb') end it '#in_optional_args' do has_block('#in_optional_args', %{ expect(test_definition.in_optional_args(:audioslave)).to eq(:audioslave) }) end it '#use_optional' do has_block('#use_optional', %{ expect(test_definition.use_optional(:audioslave)).to eq(:audioslave) }) end # it '#var_in_optional_args' do # has_block('#var_in_optional_args', %Q{ # expect(test_definition.var_in_optional_args('Chuck')).to eq('Chuck') # }) # end # it '#method_in_optional_args' do # has_block('#method_in_optional_args', %Q{ # expect(test_definition.method_in_optional_args('I am falling')).to eq('I am falling') # }) # end it '#call_method_result_in_optional_args' do has_block('#call_method_result_in_optional_args', %{ expect(test_definition.call_method_result_in_optional_args('Missing "fall_meth.first"')).to eq('Missing "fall_meth.first"') }) end it '#resursive_method' do has_block('#recursive_method', %{ expect(test_definition.recursive_method).to eq('Exception in RSpec') }) end end ================================================ FILE: spec/generation_spec.rb ================================================ # frozen_string_literal: true describe Zapata::Revolutionist do describe '#generate_with_friendly_output' do let(:file_name) { 'app/models/test_array.rb' } context 'with the generate command' do context 'with single specified' do it 'returns a single file generation message' do output = execution_output("cd #{RAILS_TEST_APP_DIR} && bundle exec zapata generate #{file_name} -s") expect(output.count).to eq 1 expect(output.first).to eq "Its done, comrades. File spec/models/test_array_spec.rb was generated.\n" end end context 'with no single specified' do it 'returns mulptile files generation messages' do output = execution_output("cd #{RAILS_TEST_APP_DIR} && bundle exec zapata generate #{file_name}") expect(output.count).to be > 1 end end end end end ================================================ FILE: spec/hash_spec.rb ================================================ # frozen_string_literal: true describe Zapata::Revolutionist do before(:all) do @generated = exec_generation('app/models/test_hash.rb') end it '#test_in_arg' do has_block('#test_in_arg', %{ expect(test_hash.test_in_arg({ 1 => :one, TestHash => 2.718 })).to eq({ 1 => :one, TestHash => 2.718 }) }) end it '#test_nested_one_level' do has_block('#test_nested_one_level', %{ expect(test_hash.test_nested_one_level({ first_level: { 1 => :one, TestHash => 2.718 } })).to eq({ first_level: { 1 => :one, TestHash => 2.718 } }) }) end it '#test_nested_two_levels' do has_block('#test_nested_two_levels', %{ expect(test_hash.test_nested_two_levels({ second_level: { first_level: { 1 => :one, TestHash => 2.718 } } })).to eq({ second_level: { first_level: { 1 => :one, TestHash => 2.718 } } }) }) end it '#test_nested_three_levels' do has_block('#test_nested_three_levels', %{ expect(test_hash.test_nested_three_levels({ third_level: { second_level: { first_level: { 1 => :one, TestHash => 2.718 } } } })).to eq({ third_level: { second_level: { first_level: { 1 => :one, TestHash => 2.718 } } } }) }) end it '#test_key_as_another_hash' do has_block('#test_key_as_another_hash', %{ expect(test_hash.test_key_as_another_hash({ { 1 => :one, TestHash => 2.718 } => :ratm })).to eq({ { 1 => :one, TestHash => 2.718 } => :ratm }) }) end it '#test_keys_are_symbols' do has_block('#test_keys_are_symbols', %{ expect(test_hash.test_keys_are_symbols({ this: 'should', be: 'pretty' })).to eq({ be: 'pretty', this: 'should' }) }) end end ================================================ FILE: spec/klass_types_spec.rb ================================================ # frozen_string_literal: true describe Zapata::Revolutionist do context 'it should work with' do it 'bare module' do generated = exec_generation('app/models/testing_module/bare.rb') expected = expected(%{require 'rails_helper' describe TestingModule::Bare do let(:bare) do TestingModule::Bare.new end end}) expect(generated).to eq(expected) end it 'nested module' do generated = exec_generation('app/models/testing_module/nested/inside.rb') expected = expected(%{require 'rails_helper' describe TestingModule::Nested::Inside do let(:inside) do TestingModule::Nested::Inside.new end end}) expect(generated).to eq(expected) end context 'klass methods' do before(:all) do @generated = exec_generation('app/models/testing_module/klass_methods.rb') end it '#defined_with_self' do has_block('#defined_with_self', %{ expect(TestingModule::KlassMethods.defined_with_self(5)).to eq(5) }) end it '#defined_with_back_back_self' do has_block('#defined_with_back_back_self', %{ expect(TestingModule::KlassMethods.defined_with_back_back_self(5)).to eq(5) }) end it '#back_to_public_defined_with_self' do has_block('#back_to_public_defined_with_self', %{ expect(TestingModule::KlassMethods.back_to_public_defined_with_self(5)).to eq(5) }) end end end end ================================================ FILE: spec/send_spec.rb ================================================ # frozen_string_literal: true describe Zapata::Revolutionist do before(:all) do @generated = exec_generation('app/models/test_send.rb') end it '#another_method_as_arg' do has_block('#another_method_as_arg', %{ expect(test_send.another_method_as_arg('Help method')).to eq('Help method') }) end # it '#second_level_method_chain' do # has_block('#second_level_method_chain', %Q{ # expect(test_send.second_level_method_chain('Help method')).to eq('Help method') # }) # end # it '#third_level_method_chain' do # has_block('#third_level_method_chain', %Q{ # expect(test_send.third_level_method_chain('Help method')).to eq('Help method') # }) # end # it '#method_with_calculated_value' do # has_block('#method_with_calculated_value', %Q{ # expect(test_send.method_with_calculated_value('Missing "calculated_value"')).to eq('Missing "calculated_value"') # }) # end it '#to_another_object' do has_block('#to_another_object', %{ expect(test_send.to_another_object(AnotherObject.my_name)).to eq('Domas') }) end it '#to_another_object_with_params' do has_block('#to_another_object_with_params', %{ expect(test_send.to_another_object_with_params(AnotherObject.send_with_params(12))).to eq('Id was 12') }) end it '#not_explicit_with_params' do has_block('#not_explicit_with_params', %{ expect(test_send.not_explicit_with_params('Could you find it?')).to eq('Could you find it?') }) end it '#fail_to_understand' do has_block('#fail_to_understand', %{ expect(test_send.fail_to_understand('Missing "failure"')).to eq('Missing "failure"') }) end end ================================================ FILE: spec/simple_types_spec.rb ================================================ # frozen_string_literal: true describe Zapata::Revolutionist do context 'it should work with' do it 'ints' do generated = exec_generation('app/models/test_int.rb') expected = expected(%{require 'rails_helper' describe TestInt do let(:test_int) do TestInt.new end it '#test_int_in_arg' do expect(test_int.test_int_in_arg(1)).to eq(1) end end}) expect(generated).to eq(expected) end it 'symbols' do generated = exec_generation('app/models/test_sym.rb') expected = expected(%{require 'rails_helper' describe TestSym do let(:test_sym) do TestSym.new end it '#test_sym_in_arg' do expect(test_sym.test_sym_in_arg(:rock)).to eq(:rock) end end}) expect(generated).to eq(expected) end it 'strings' do generated = exec_generation('app/models/test_str.rb') expected = expected(%{require 'rails_helper' describe TestStr do let(:test_str) do TestStr.new end it '#test_str_in_arg' do expect(test_str.test_str_in_arg('audioslave')).to eq('audioslave') end end}) expect(generated).to eq(expected) end it 'floats' do generated = exec_generation('app/models/test_float.rb') expected = expected(%{require 'rails_helper' describe TestFloat do let(:test_float) do TestFloat.new end it '#test_float_in_arg' do expect(test_float.test_float_in_arg(2.718)).to eq(2.718) end end}) expect(generated).to eq(expected) end it 'consts' do generated = exec_generation('app/models/test_const.rb') expected = expected(%{require 'rails_helper' describe TestConst do let(:test_const) do TestConst.new end it '#test_const_in_arg' do expect(test_const.test_const_in_arg(TestConst)).to eq(TestConst) end end}) expect(generated).to eq(expected) end end end ================================================ FILE: spec/spec_helper.rb ================================================ # frozen_string_literal: true require 'coveralls' Coveralls.wear! require 'zapata' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = '.rspec_status' config.expect_with :rspec do |c| c.syntax = :expect end end # Helper methods RAILS_TEST_APP_DIR = "#{Dir.pwd}/spec/support/rails_test_app" def execution_output(command) stdout = Bundler.with_clean_env do Open3.pipeline_r( command ) end stdout.first.readlines end def clean(string) string.split(/\n/).map(&:strip).join("\n") end def expected(code) clean( <<-EXPECTED #{code} EXPECTED ) end def exec_generation(generate_for) _, stdout, stderr = Bundler.with_clean_env do Open3.popen3( "cd #{RAILS_TEST_APP_DIR} && bundle exec zapata generate #{generate_for} -s" ) end output = stdout.readlines begin generated_filename = output.last.match(/File\ (.+)\ was/)[1] rescue StandardError raise "Did not get the message that file was generated. Got this instead: STDOUT: #{output} STDERR: #{stderr.readlines}" end spec_path = "#{RAILS_TEST_APP_DIR}/#{generated_filename}" clean( <<-ACTUAL #{File.read(spec_path)} ACTUAL ) end def has_block(name, expected_content) generated_lines = @generated.split("\n") it_starts = generated_lines.index { |line| line == "it '#{name}' do" } raise 'No such block' unless it_starts might_match_lines = generated_lines[it_starts..-1] it_ends = might_match_lines.index { |line| line == 'end' } block = might_match_lines[1...it_ends].first expect(block).to eq(expected_content.strip) end ================================================ FILE: spec/support/rails_test_app/README.md ================================================ # Zapata Rails test Just a testing app for [Zapata gem](https://github.com/Nedomas/zapata) ================================================ FILE: spec/support/rails_test_app/Rakefile ================================================ # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require File.expand_path('../config/application', __FILE__) Rails.application.load_tasks ================================================ FILE: spec/support/rails_test_app/app/assets/config/manifest.js ================================================ //= link_tree ../images //= link_directory ../javascripts .js //= link_directory ../stylesheets .css ================================================ FILE: spec/support/rails_test_app/app/assets/images/.keep ================================================ ================================================ FILE: spec/support/rails_test_app/app/assets/javascripts/application.js ================================================ // This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. // // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details // about supported directives. // //= require jquery //= require jquery_ujs //= require turbolinks //= require_tree . ================================================ FILE: spec/support/rails_test_app/app/assets/stylesheets/application.css ================================================ /* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the bottom of the * compiled file so the styles you add here take precedence over styles defined in any styles * defined in the other CSS/SCSS files in this directory. It is generally better to create a new * file per style scope. * *= require_tree . *= require_self */ ================================================ FILE: spec/support/rails_test_app/app/controllers/application_controller.rb ================================================ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception end ================================================ FILE: spec/support/rails_test_app/app/controllers/concerns/.keep ================================================ ================================================ FILE: spec/support/rails_test_app/app/helpers/application_helper.rb ================================================ module ApplicationHelper end ================================================ FILE: spec/support/rails_test_app/app/mailers/.keep ================================================ ================================================ FILE: spec/support/rails_test_app/app/models/.keep ================================================ ================================================ FILE: spec/support/rails_test_app/app/models/concerns/.keep ================================================ ================================================ FILE: spec/support/rails_test_app/app/models/robot_to_test.rb ================================================ class RobotToTest def initialize(human_name, _cv) @name = robot_name(human_name) end def robot_name(human_name) "#{self.class.prefix}_#{human_name}" end def cv { planets: planets } end def nested_fun_objects(_fun_objects) 'It was fun' end def self.prefix 'Robot' end private def planets ['Mars', Human.home] end def fun_objects [%i[array in array], { hash: nested_hash }] end def nested_hash { in_hash: { in: array } } end def array %w[array] end end class Human def initialize human_name = 'Emiliano' end def self.home 'Earth' end end ================================================ FILE: spec/support/rails_test_app/app/models/test_array.rb ================================================ class TestArray def initialize end def test_in_arg(numbers_array) numbers_array end def test_nested_one_level(nested_one_level) nested_one_level end def test_nested_two_levels(nested_two_levels) nested_two_levels end def test_nested_three_levels(nested_three_levels) nested_three_levels end def test_hash_nested(hash_nested) hash_nested end private def data_to_analyze numbers_array = [2, 7.1, 8] nested_one_level = [numbers_array, :mexico] nested_two_levels = [nested_one_level, numbers_array, :mexico] nested_three_levels = [nested_two_levels, nested_one_level, numbers_array, :mexico] hash_nested = [{ emiliano: numbers_array }] end end ================================================ FILE: spec/support/rails_test_app/app/models/test_const.rb ================================================ class TestConst def initialize end def test_const_in_arg(const) const end private def data_to_analyze const = TestConst end end ================================================ FILE: spec/support/rails_test_app/app/models/test_definition.rb ================================================ class TestDefinition def in_optional_args(optional = :audioslave) optional end def use_optional(optional) optional end def var_in_optional_args(optional_var = fallback) optional_var end def method_in_optional_args(optional_method = fall_meth) optional_method end def call_method_result_in_optional_args(complex_method = fall_meth.first) complex_method end def should_not_show_empty_method end def recursive_method recursive_method end private def fall_meth 'I am falling' end def data_to_analyze fallback = 'Chuck' end end ================================================ FILE: spec/support/rails_test_app/app/models/test_float.rb ================================================ class TestFloat def initialize end def test_float_in_arg(float) float end private def data_to_analyze float = 2.718 end end ================================================ FILE: spec/support/rails_test_app/app/models/test_hash.rb ================================================ class TestHash def initialize end def test_in_arg(hash) hash end def test_nested_one_level(one_level_nested_hash) one_level_nested_hash end def test_nested_two_levels(two_levels_nested_hash) two_levels_nested_hash end def test_nested_three_levels(three_levels_nested_hash) three_levels_nested_hash end def test_key_as_another_hash(key_as_another_hash) key_as_another_hash end def test_keys_are_symbols(pretty_hash) pretty_hash end private def data_to_analyze hash = { 1 => :one, TestHash => 2.718 } one_level_nested_hash = { first_level: hash } two_levels_nested_hash = { second_level: one_level_nested_hash } three_levels_nested_hash = { third_level: two_levels_nested_hash } key_as_another_hash = { hash => :ratm } pretty_hash = { this: 'should', be: 'pretty' } end end ================================================ FILE: spec/support/rails_test_app/app/models/test_int.rb ================================================ class TestInt def initialize end def test_int_in_arg(int) int end private def data_to_analyze int = 1 end end ================================================ FILE: spec/support/rails_test_app/app/models/test_send.rb ================================================ class TestSend def another_method_as_arg(help_method) help_method end def second_level_method_chain(second_level_method) second_level_method end def third_level_method_chain(third_level_method) third_level_method end def method_with_calculated_value(calculated_value) calculated_value end def to_another_object(another_object_method) another_object_method end def to_another_object_with_params(send_with_params) send_with_params end def not_explicit_with_params(not_explicit) not_explicit end def fail_to_understand(failure) failure end private def help_method 'Help method' end def second_level_method help_method end def third_level_method second_level_method end def calculated_value 1 + 3 end def not_explicit(_name) 'Could you find it?' end def failure(name) # does not understand :dstr "Can't you understand, #{name}?" end def data_to_analyze another_object_method = AnotherObject.my_name send_with_params = AnotherObject.send_with_params(12) end end class AnotherObject def self.my_name 'Domas' end def self.send_with_params(id) "Id was #{id}" end end ================================================ FILE: spec/support/rails_test_app/app/models/test_str.rb ================================================ class TestStr def initialize end def test_str_in_arg(str) str end private def data_to_analyze str = 'audioslave' end end ================================================ FILE: spec/support/rails_test_app/app/models/test_sym.rb ================================================ class TestSym def initialize end def test_sym_in_arg(sym) sym end private def data_to_analyze sym = :rock end end ================================================ FILE: spec/support/rails_test_app/app/models/testing_module/bare.rb ================================================ module TestingModule class Bare def initialize end end end ================================================ FILE: spec/support/rails_test_app/app/models/testing_module/klass_methods.rb ================================================ module TestingModule class KlassMethods def self.defined_with_self(klass_methods_int) klass_methods_int end class << self def defined_with_back_back_self(klass_methods_int) klass_methods_int end end private class << self def privately_defined_with_back_back_self(klass_methods_int) klass_methods_int end end def self.privately_defined_with_self(klass_methods_int) klass_methods_int end protected class << self def protectedly_defined_with_back_back_self(klass_methods_int) klass_methods_int end end def self.protectedly_defined_with_self(klass_methods_int) klass_methods_int end def data_to_analyze klass_methods_int = 5 end public def self.back_to_public_defined_with_self(klass_methods_int) klass_methods_int end end end ================================================ FILE: spec/support/rails_test_app/app/models/testing_module/nested/inside.rb ================================================ module TestingModule module Nested class Inside def initialize end end end end ================================================ FILE: spec/support/rails_test_app/app/views/layouts/application.html.erb ================================================
You may have mistyped the address or the page may have moved.
If you are the application owner check the logs for more information.
Maybe you tried to change something you didn't have access to.
If you are the application owner check the logs for more information.
If you are the application owner check the logs for more information.