Module: RailsFields::ClassMethods

Defined in:
lib/rails_fields/class_methods.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#nametype

Declares a field with enforcement.

Returns:

  • (type)

    the name property



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/rails_fields/class_methods.rb', line 33

def field(name, type, null: true, index: false)
  # Check if type is a valid GraphQL type
  # GraphQL::Types.const_get(type) if type.is_a?(Symbol) || type.is_a?(String)
  unless Utils.valid_type?(type)
    raise Errors::RailsFieldsUnknownTypeError.new("
      Declared field '#{name}' in class '#{self.name}' of unknown type '#{type}'. Allowed types are: #{Utils.allowed_types.join(', ')}.
    ")
  end

  declared_fields << DeclaredField.new(name: name.to_s, type:, null:, index:)
end

Instance Method Details

#declared_fieldsObject

TODO: Check all models at rails init app? like migrations?



8
9
10
# File 'lib/rails_fields/class_methods.rb', line 8

def declared_fields
  @declared_fields ||= []
end

#declared_fields=(value) ⇒ Object



12
13
14
# File 'lib/rails_fields/class_methods.rb', line 12

def declared_fields=(value)
  @declared_fields = value
end

#enforce_declared_fieldsObject



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/rails_fields/class_methods.rb', line 95

def enforce_declared_fields
  database_columns = column_names.map(&:to_sym)
  declared_fields_names = declared_fields.map(&:name).map(&:to_sym) || []
  changes = RailsFields::Utils.detect_changes(self)
  migration = RailsFields::Utils.generate_migration(self, changes)
  instance_methods = self.instance_methods(false).select do |method|
    instance_method(method).source_location.first.start_with?(Rails.root.to_s)
  end
  extra_methods = instance_methods - declared_fields_names.map(&:to_sym)
  has_changes = !changes.nil?

  unless extra_methods.empty?
    # TODO: Custom error subclass
    raise "You have extra methods declared in #{name}: #{extra_methods.join(', ')}. Please remove them or declare them as fields."
  end

  if has_changes
    error_message = <<~STRING

      ----------------

      Declared Fields:
      #{declared_fields_names.join(', ')}

      Database columns:
      #{database_columns.join(', ')}

      Changes:
      #{changes.to_yaml.lines[1..-1].join}
      Migration:
      #{migration}

      ----------------
    STRING
    raise Errors::RailsFieldsMismatchError.new(error_message)
  end
end

#field(name, type, null: true, index: false) ⇒ type

Declares a field with enforcement.

Returns:

  • (type)

    the name property



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/rails_fields/class_methods.rb', line 33

def field(name, type, null: true, index: false)
  # Check if type is a valid GraphQL type
  # GraphQL::Types.const_get(type) if type.is_a?(Symbol) || type.is_a?(String)
  unless Utils.valid_type?(type)
    raise Errors::RailsFieldsUnknownTypeError.new("
      Declared field '#{name}' in class '#{self.name}' of unknown type '#{type}'. Allowed types are: #{Utils.allowed_types.join(', ')}.
    ")
  end

  declared_fields << DeclaredField.new(name: name.to_s, type:, null:, index:)
end

#gql_typeObject



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rails_fields/class_methods.rb', line 45

def gql_type
  unless defined?(GraphQL)
    raise "GraphQL is not available. Add `gem 'graphql'` and install it, or avoid calling `gql_type`."
  end
  return RailsFields.processed_classes[self] if RailsFields.processed_classes[self].present?

  fields = declared_fields
  owner_self = self

  base_object = defined?(::Types::BaseObject) ? ::Types::BaseObject : ::GraphQL::Schema::Object
  type = Class.new(base_object) do
    # graphql_name "#{owner_self.name}Type"
    graphql_name "#{owner_self.name}"
    description "A type representing a #{owner_self.name}"

    fields.each do |f|
      next if f.type.nil? # TODO: ! remove references fields

      # Assuming a proper mapping from your custom types to GraphQL types
      # TODO: use a better method or block
      field_name = f.name.to_s
      field_gql_type = field_name == 'id' ? GraphQL::Types::ID : Utils::RAILS_TO_GQL_TYPE_MAP[f.type]
      field field_name, field_gql_type
    end
  end

  # Cache the processed class here to prevent infinite recursion
  RailsFields.processed_classes[self] = type

  type.instance_eval do
    owner_self.reflections.each do |association_name, reflection|
      if reflection.macro == :has_many
        reflection_klass = if reflection.options[:through]
          through_reflection_klass = reflection.through_reflection.klass
          source_reflection_name = reflection.source_reflection_name.to_s
          source_reflection = through_reflection_klass.reflections[source_reflection_name]
          source_reflection ? source_reflection.klass : through_reflection_klass
        else
          reflection.klass
        end
        field association_name, [reflection_klass.gql_type], null: true
      elsif reflection.macro == :belongs_to
        field association_name, reflection.klass.gql_type, null: true
      end
    end

    type
  end
end

#write_migration(index: nil) ⇒ Object



16
17
18
19
# File 'lib/rails_fields/class_methods.rb', line 16

def write_migration(index: nil)
  changes = RailsFields::Utils.detect_changes(self)
  RailsFields::Utils.generate_migration(self, changes, index:, write: true)
end